Added Mandelbrot Video
This commit is contained in:
		
							parent
							
								
									64c3ec7f27
								
							
						
					
					
						commit
						deaadfef4a
					
				
							
								
								
									
										592
									
								
								Videos/OneLoneCoder_PGE_Mandelbrot.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										592
									
								
								Videos/OneLoneCoder_PGE_Mandelbrot.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,592 @@ | |||||||
|  | /*
 | ||||||
|  | 	Brute Force Processing a Mandelbrot Renderer | ||||||
|  | 	"Dammit Moros & Saladin, you guys keep making tools, I'll have nothing left to video..." - javidx9 | ||||||
|  | 
 | ||||||
|  | 	License (OLC-3) | ||||||
|  | 	~~~~~~~~~~~~~~~ | ||||||
|  | 
 | ||||||
|  | 	Copyright 2018-2020 OneLoneCoder.com | ||||||
|  | 
 | ||||||
|  | 	Redistribution and use in source and binary forms, with or without | ||||||
|  | 	modification, are permitted provided that the following conditions | ||||||
|  | 	are met: | ||||||
|  | 
 | ||||||
|  | 	1. Redistributions or derivations of source code must retain the above | ||||||
|  | 	copyright notice, this list of conditions and the following disclaimer. | ||||||
|  | 
 | ||||||
|  | 	2. Redistributions or derivative works in binary form must reproduce | ||||||
|  | 	the above copyright notice. This list of conditions and the following | ||||||
|  | 	disclaimer must be reproduced in the documentation and/or other | ||||||
|  | 	materials provided with the distribution. | ||||||
|  | 
 | ||||||
|  | 	3. Neither the name of the copyright holder nor the names of its | ||||||
|  | 	contributors may be used to endorse or promote products derived | ||||||
|  | 	from this software without specific prior written permission. | ||||||
|  | 
 | ||||||
|  | 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  | 	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||||
|  | 	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||||
|  | 	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||||
|  | 	HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  | 	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | 	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  | 	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  | 	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | 	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | 	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | 
 | ||||||
|  | 	Relevant Video: https://youtu.be/PBvLs88hvJ8
 | ||||||
|  | 
 | ||||||
|  | 	Links | ||||||
|  | 	~~~~~ | ||||||
|  | 	YouTube:	https://www.youtube.com/javidx9
 | ||||||
|  | 				https://www.youtube.com/javidx9extra
 | ||||||
|  | 	Discord:	https://discord.gg/WhwHUMV
 | ||||||
|  | 	Twitter:	https://www.twitter.com/javidx9
 | ||||||
|  | 	Twitch:		https://www.twitch.tv/javidx9
 | ||||||
|  | 	GitHub:		https://www.github.com/onelonecoder
 | ||||||
|  | 	Patreon:	https://www.patreon.com/javidx9
 | ||||||
|  | 	Homepage:	https://www.onelonecoder.com
 | ||||||
|  | 
 | ||||||
|  | 	Community Blog: https://community.onelonecoder.com
 | ||||||
|  | 
 | ||||||
|  | 	Author | ||||||
|  | 	~~~~~~ | ||||||
|  | 	David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define OLC_PGE_APPLICATION | ||||||
|  | #include "olcPixelGameEngine.h" | ||||||
|  | 
 | ||||||
|  | #include <condition_variable> | ||||||
|  | #include <atomic> | ||||||
|  | #include <complex> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <immintrin.h> | ||||||
|  | 
 | ||||||
|  | constexpr int nMaxThreads = 32; | ||||||
|  | 
 | ||||||
|  | class olcFractalExplorer : public olc::PixelGameEngine | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	olcFractalExplorer() | ||||||
|  | 	{ | ||||||
|  | 		sAppName = "Brute Force Processing"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int* pFractal = nullptr; | ||||||
|  | 	int nMode = 4; | ||||||
|  | 	int nIterations = 128; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	bool OnUserCreate() override | ||||||
|  | 	{ | ||||||
|  | 		//pFractal = new int[ScreenWidth() * ScreenHeight()]{ 0 };
 | ||||||
|  | 
 | ||||||
|  | 		// Using Vector extensions, align memory (not as necessary as it used to be)
 | ||||||
|  | 		// MS Specific - see std::aligned_alloc for others
 | ||||||
|  | 		pFractal = (int*)_aligned_malloc(size_t(ScreenWidth()) * size_t(ScreenHeight()) * sizeof(int), 64); | ||||||
|  | 
 | ||||||
|  | 		InitialiseThreadPool(); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool OnUserDestroy() override | ||||||
|  | 	{ | ||||||
|  | 		// Stop Worker threads
 | ||||||
|  | 		for (int i = 0; i < nMaxThreads; i++) | ||||||
|  | 		{ | ||||||
|  | 			workers[i].alive = false;		 // Allow thread exit
 | ||||||
|  | 			workers[i].cvStart.notify_one(); // Fake starting gun
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Clean up worker threads
 | ||||||
|  | 		for (int i = 0; i < nMaxThreads; i++) | ||||||
|  | 			workers[i].thread.join(); | ||||||
|  | 
 | ||||||
|  | 		// Clean up memory
 | ||||||
|  | 		_aligned_free(pFractal); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Method 1) - Super simple, no effort at optimising
 | ||||||
|  | 	void CreateFractalBasic(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations) | ||||||
|  | 	{ | ||||||
|  | 		double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x)); | ||||||
|  | 		double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y)); | ||||||
|  | 		 | ||||||
|  | 		for (int y = pix_tl.y; y < pix_br.y; y++) | ||||||
|  | 		{ | ||||||
|  | 			for (int x = pix_tl.x; x < pix_br.x; x++) | ||||||
|  | 			{ | ||||||
|  | 				std::complex<double> c(x * x_scale + frac_tl.x, y * y_scale + frac_tl.y); | ||||||
|  | 				std::complex<double> z(0, 0); | ||||||
|  | 
 | ||||||
|  | 				int n = 0; | ||||||
|  | 				while (abs(z) < 2.0 && n < iterations) | ||||||
|  | 				{ | ||||||
|  | 					z = (z * z) + c; | ||||||
|  | 					n++; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				pFractal[y * ScreenWidth() + x] = n; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Method 2) - Attempt to pre-calculate as much as possible, and reduce
 | ||||||
|  | 	// repeated multiplications
 | ||||||
|  | 	void CreateFractalPreCalculate(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations) | ||||||
|  | 	{ | ||||||
|  | 		double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x)); | ||||||
|  | 		double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y)); | ||||||
|  | 
 | ||||||
|  | 		double x_pos = frac_tl.x; | ||||||
|  | 		double y_pos = frac_tl.y; | ||||||
|  | 
 | ||||||
|  | 		int y_offset = 0; | ||||||
|  | 		int row_size = pix_br.x - pix_tl.x; | ||||||
|  | 
 | ||||||
|  | 		int x, y, n; | ||||||
|  | 		std::complex<double> c, z; | ||||||
|  | 
 | ||||||
|  | 		for (y = pix_tl.y; y < pix_br.y; y++) | ||||||
|  | 		{ | ||||||
|  | 			x_pos = frac_tl.x; | ||||||
|  | 			for (x = pix_tl.x; x < pix_br.x; x++) | ||||||
|  | 			{				 | ||||||
|  | 				c = { x_pos, y_pos }; | ||||||
|  | 				z = { 0,0 }; | ||||||
|  | 
 | ||||||
|  | 				n = 0; | ||||||
|  | 				while (abs(z) < 2.0 && n < iterations) | ||||||
|  | 				{ | ||||||
|  | 					z = (z * z) + c; | ||||||
|  | 					n++; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				pFractal[y_offset + x] = n; | ||||||
|  | 				x_pos += x_scale; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			y_pos += y_scale; | ||||||
|  | 			y_offset += row_size; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Method 3) - Replace std::complex with just hard coded mathematics
 | ||||||
|  | 	void CreateFractalNoComplex(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations) | ||||||
|  | 	{ | ||||||
|  | 		double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x)); | ||||||
|  | 		double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y)); | ||||||
|  | 
 | ||||||
|  | 		double x_pos = frac_tl.x; | ||||||
|  | 		double y_pos = frac_tl.y; | ||||||
|  | 
 | ||||||
|  | 		int y_offset = 0; | ||||||
|  | 		int row_size = ScreenWidth(); | ||||||
|  | 
 | ||||||
|  | 		int x, y, n; | ||||||
|  | 
 | ||||||
|  | 		double cr = 0; | ||||||
|  | 		double ci = 0; | ||||||
|  | 		double zr = 0; | ||||||
|  | 		double zi = 0; | ||||||
|  | 		double re = 0; | ||||||
|  | 		double im = 0; | ||||||
|  | 
 | ||||||
|  | 		for (y = pix_tl.y; y < pix_br.y; y++) | ||||||
|  | 		{ | ||||||
|  | 			x_pos = frac_tl.x; | ||||||
|  | 			ci = y_pos; | ||||||
|  | 			for (x = pix_tl.x; x < pix_br.x; x++) | ||||||
|  | 			{ | ||||||
|  | 				cr = x_pos; | ||||||
|  | 				zr = 0; | ||||||
|  | 				zi = 0; | ||||||
|  | 
 | ||||||
|  | 				n = 0; | ||||||
|  | 				while ((zr * zr + zi * zi) < 4.0 && n < iterations) | ||||||
|  | 				{ | ||||||
|  | 					re = zr * zr - zi * zi + cr; | ||||||
|  | 					im = zr * zi * 2.0 + ci; | ||||||
|  | 					zr = re; | ||||||
|  | 					zi = im; | ||||||
|  | 					n++; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				pFractal[y_offset + x] = n; | ||||||
|  | 				x_pos += x_scale; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			y_pos += y_scale; | ||||||
|  | 			y_offset += row_size; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Method 4) - Use AVX2 Vector co-processor to handle 4 fractal locations at once
 | ||||||
|  | 	void CreateFractalIntrinsics(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations) | ||||||
|  | 	{ | ||||||
|  | 		double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x)); | ||||||
|  | 		double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y)); | ||||||
|  | 
 | ||||||
|  | 		double y_pos = frac_tl.y; | ||||||
|  | 
 | ||||||
|  | 		int y_offset = 0; | ||||||
|  | 		int row_size = ScreenWidth(); | ||||||
|  | 
 | ||||||
|  | 		int x, y; | ||||||
|  | 
 | ||||||
|  | 		__m256d _a, _b, _two, _four, _mask1; | ||||||
|  | 		__m256d _zr, _zi, _zr2, _zi2, _cr, _ci; | ||||||
|  | 		__m256d _x_pos_offsets, _x_pos, _x_scale, _x_jump; | ||||||
|  | 		__m256i _one, _c, _n, _iterations, _mask2; | ||||||
|  | 
 | ||||||
|  | 		_one = _mm256_set1_epi64x(1); | ||||||
|  | 		_two = _mm256_set1_pd(2.0); | ||||||
|  | 		_four = _mm256_set1_pd(4.0); | ||||||
|  | 		_iterations = _mm256_set1_epi64x(iterations); | ||||||
|  | 
 | ||||||
|  | 		_x_scale = _mm256_set1_pd(x_scale); | ||||||
|  | 		_x_jump = _mm256_set1_pd(x_scale * 4); | ||||||
|  | 		_x_pos_offsets = _mm256_set_pd(0, 1, 2, 3); | ||||||
|  | 		_x_pos_offsets = _mm256_mul_pd(_x_pos_offsets, _x_scale); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		for (y = pix_tl.y; y < pix_br.y; y++) | ||||||
|  | 		{ | ||||||
|  | 			// Reset x_position
 | ||||||
|  | 			_a = _mm256_set1_pd(frac_tl.x); | ||||||
|  | 			_x_pos = _mm256_add_pd(_a, _x_pos_offsets); | ||||||
|  | 
 | ||||||
|  | 			_ci = _mm256_set1_pd(y_pos); | ||||||
|  | 
 | ||||||
|  | 			for (x = pix_tl.x; x < pix_br.x; x += 4) | ||||||
|  | 			{ | ||||||
|  | 				_cr = _x_pos; | ||||||
|  | 				_zr = _mm256_setzero_pd(); | ||||||
|  | 				_zi = _mm256_setzero_pd(); | ||||||
|  | 				_n = _mm256_setzero_si256(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 			repeat: | ||||||
|  | 				_zr2 = _mm256_mul_pd(_zr, _zr); | ||||||
|  | 				_zi2 = _mm256_mul_pd(_zi, _zi); | ||||||
|  | 				_a = _mm256_sub_pd(_zr2, _zi2); | ||||||
|  | 				_a = _mm256_add_pd(_a, _cr); | ||||||
|  | 				_b = _mm256_mul_pd(_zr, _zi); | ||||||
|  | 				_b = _mm256_fmadd_pd(_b, _two, _ci); | ||||||
|  | 				_zr = _a; | ||||||
|  | 				_zi = _b; | ||||||
|  | 				_a = _mm256_add_pd(_zr2, _zi2); | ||||||
|  | 				_mask1 = _mm256_cmp_pd(_a, _four, _CMP_LT_OQ); | ||||||
|  | 				_mask2 = _mm256_cmpgt_epi64(_iterations, _n); | ||||||
|  | 				_mask2 = _mm256_and_si256(_mask2, _mm256_castpd_si256(_mask1)); | ||||||
|  | 				_c = _mm256_and_si256(_one, _mask2); // Zero out ones where n < iterations													
 | ||||||
|  | 				_n = _mm256_add_epi64(_n, _c); // n++ Increase all n
 | ||||||
|  | 				if (_mm256_movemask_pd(_mm256_castsi256_pd(_mask2)) > 0) | ||||||
|  | 					goto repeat; | ||||||
|  | 
 | ||||||
|  | 				pFractal[y_offset + x + 0] = int(_n.m256i_i64[3]); | ||||||
|  | 				pFractal[y_offset + x + 1] = int(_n.m256i_i64[2]); | ||||||
|  | 				pFractal[y_offset + x + 2] = int(_n.m256i_i64[1]); | ||||||
|  | 				pFractal[y_offset + x + 3] = int(_n.m256i_i64[0]); | ||||||
|  | 				_x_pos = _mm256_add_pd(_x_pos, _x_jump); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			y_pos += y_scale; | ||||||
|  | 			y_offset += row_size; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Method 5) - Spawn threads that use AVX method above
 | ||||||
|  | 	void CreateFractalThreads(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations) | ||||||
|  | 	{ | ||||||
|  | 		int nSectionWidth = (pix_br.x - pix_tl.x) / nMaxThreads; | ||||||
|  | 		double dFractalWidth = (frac_br.x - frac_tl.x) / double(nMaxThreads); | ||||||
|  | 
 | ||||||
|  | 		std::thread t[nMaxThreads]; | ||||||
|  | 
 | ||||||
|  | 		for (size_t i = 0; i < nMaxThreads; i++) | ||||||
|  | 			t[i] = std::thread(&olcFractalExplorer::CreateFractalIntrinsics, this, | ||||||
|  | 				olc::vi2d(pix_tl.x + nSectionWidth * (i), pix_tl.y), | ||||||
|  | 				olc::vi2d(pix_tl.x + nSectionWidth * (i + 1), pix_br.y), | ||||||
|  | 				olc::vd2d(frac_tl.x + dFractalWidth * double(i), frac_tl.y), | ||||||
|  | 				olc::vd2d(frac_tl.x + dFractalWidth * double(i + 1), frac_br.y), | ||||||
|  | 				iterations); | ||||||
|  | 
 | ||||||
|  | 		for (size_t i = 0; i < nMaxThreads; i++) | ||||||
|  | 			t[i].join(); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Method 6) - Threadpool, keep threads alive and reuse them, reducing setup overhead
 | ||||||
|  | 	struct WorkerThread | ||||||
|  | 	{ | ||||||
|  | 		olc::vi2d pix_tl = { 0,0 }; | ||||||
|  | 		olc::vi2d pix_br = { 0,0 }; | ||||||
|  | 		olc::vd2d frac_tl = { 0,0 }; | ||||||
|  | 		olc::vd2d frac_br = { 0,0 }; | ||||||
|  | 		int iterations = 0; | ||||||
|  | 		std::condition_variable cvStart; | ||||||
|  | 		bool alive = true; | ||||||
|  | 		std::mutex mux; | ||||||
|  | 		int screen_width = 0; | ||||||
|  | 		int* fractal = nullptr; | ||||||
|  | 
 | ||||||
|  | 		std::thread thread; | ||||||
|  | 
 | ||||||
|  | 		void Start(const olc::vi2d& ptl, const olc::vi2d& pbr, const olc::vd2d& ftl, const olc::vd2d& fbr, const int it) | ||||||
|  | 		{ | ||||||
|  | 			pix_tl = ptl; | ||||||
|  | 			pix_br = pbr; | ||||||
|  | 			frac_tl = ftl; | ||||||
|  | 			frac_br = fbr; | ||||||
|  | 			iterations = it; | ||||||
|  | 			std::unique_lock<std::mutex> lm(mux); | ||||||
|  | 			cvStart.notify_one(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void CreateFractal() | ||||||
|  | 		{ | ||||||
|  | 			while (alive) | ||||||
|  | 			{ | ||||||
|  | 				std::unique_lock<std::mutex> lm(mux); | ||||||
|  | 				cvStart.wait(lm); | ||||||
|  | 
 | ||||||
|  | 				double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x)); | ||||||
|  | 				double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y)); | ||||||
|  | 
 | ||||||
|  | 				double y_pos = frac_tl.y; | ||||||
|  | 
 | ||||||
|  | 				int y_offset = 0; | ||||||
|  | 				int row_size = screen_width; | ||||||
|  | 
 | ||||||
|  | 				int x, y; | ||||||
|  | 
 | ||||||
|  | 				__m256d _a, _b, _two, _four, _mask1; | ||||||
|  | 				__m256d _zr, _zi, _zr2, _zi2, _cr, _ci; | ||||||
|  | 				__m256d _x_pos_offsets, _x_pos, _x_scale, _x_jump; | ||||||
|  | 				__m256i _one, _c, _n, _iterations, _mask2; | ||||||
|  | 
 | ||||||
|  | 				_one = _mm256_set1_epi64x(1); | ||||||
|  | 				_two = _mm256_set1_pd(2.0); | ||||||
|  | 				_four = _mm256_set1_pd(4.0); | ||||||
|  | 				_iterations = _mm256_set1_epi64x(iterations); | ||||||
|  | 
 | ||||||
|  | 				_x_scale = _mm256_set1_pd(x_scale); | ||||||
|  | 				_x_jump = _mm256_set1_pd(x_scale * 4); | ||||||
|  | 				_x_pos_offsets = _mm256_set_pd(0, 1, 2, 3); | ||||||
|  | 				_x_pos_offsets = _mm256_mul_pd(_x_pos_offsets, _x_scale); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				for (y = pix_tl.y; y < pix_br.y; y++) | ||||||
|  | 				{ | ||||||
|  | 					// Reset x_position
 | ||||||
|  | 					_a = _mm256_set1_pd(frac_tl.x); | ||||||
|  | 					_x_pos = _mm256_add_pd(_a, _x_pos_offsets); | ||||||
|  | 
 | ||||||
|  | 					_ci = _mm256_set1_pd(y_pos); | ||||||
|  | 
 | ||||||
|  | 					for (x = pix_tl.x; x < pix_br.x; x += 4) | ||||||
|  | 					{ | ||||||
|  | 						_cr = _x_pos; | ||||||
|  | 						_zr = _mm256_setzero_pd(); | ||||||
|  | 						_zi = _mm256_setzero_pd(); | ||||||
|  | 						_n = _mm256_setzero_si256(); | ||||||
|  | 
 | ||||||
|  | 					repeat: | ||||||
|  | 						_zr2 = _mm256_mul_pd(_zr, _zr); | ||||||
|  | 						_zi2 = _mm256_mul_pd(_zi, _zi); | ||||||
|  | 						_a = _mm256_sub_pd(_zr2, _zi2); | ||||||
|  | 						_a = _mm256_add_pd(_a, _cr); | ||||||
|  | 						_b = _mm256_mul_pd(_zr, _zi); | ||||||
|  | 						_b = _mm256_fmadd_pd(_b, _two, _ci); | ||||||
|  | 						_zr = _a; | ||||||
|  | 						_zi = _b; | ||||||
|  | 						_a = _mm256_add_pd(_zr2, _zi2); | ||||||
|  | 						_mask1 = _mm256_cmp_pd(_a, _four, _CMP_LT_OQ); | ||||||
|  | 						_mask2 = _mm256_cmpgt_epi64(_iterations, _n); | ||||||
|  | 						_mask2 = _mm256_and_si256(_mask2, _mm256_castpd_si256(_mask1)); | ||||||
|  | 						_c = _mm256_and_si256(_one, _mask2); // Zero out ones where n < iterations													
 | ||||||
|  | 						_n = _mm256_add_epi64(_n, _c); // n++ Increase all n
 | ||||||
|  | 						if (_mm256_movemask_pd(_mm256_castsi256_pd(_mask2)) > 0) | ||||||
|  | 							goto repeat; | ||||||
|  | 
 | ||||||
|  | 						fractal[y_offset + x + 0] = int(_n.m256i_i64[3]); | ||||||
|  | 						fractal[y_offset + x + 1] = int(_n.m256i_i64[2]); | ||||||
|  | 						fractal[y_offset + x + 2] = int(_n.m256i_i64[1]); | ||||||
|  | 						fractal[y_offset + x + 3] = int(_n.m256i_i64[0]); | ||||||
|  | 						_x_pos = _mm256_add_pd(_x_pos, _x_jump); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					y_pos += y_scale; | ||||||
|  | 					y_offset += row_size; | ||||||
|  | 				} | ||||||
|  | 				nWorkerComplete++; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	WorkerThread workers[nMaxThreads]; | ||||||
|  | 	static std::atomic<int> nWorkerComplete; | ||||||
|  | 
 | ||||||
|  | 	void InitialiseThreadPool() | ||||||
|  | 	{ | ||||||
|  | 		for (int i = 0; i < nMaxThreads; i++) | ||||||
|  | 		{ | ||||||
|  | 			workers[i].alive = true; | ||||||
|  | 			workers[i].fractal = pFractal; | ||||||
|  | 			workers[i].screen_width = ScreenWidth(); | ||||||
|  | 			workers[i].thread = std::thread(&WorkerThread::CreateFractal, &workers[i]); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void CreateFractalThreadPool(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations) | ||||||
|  | 	{ | ||||||
|  | 		int nSectionWidth = (pix_br.x - pix_tl.x) / nMaxThreads; | ||||||
|  | 		double dFractalWidth = (frac_br.x - frac_tl.x) / double(nMaxThreads); | ||||||
|  | 		nWorkerComplete = 0; | ||||||
|  | 
 | ||||||
|  | 		for (size_t i = 0; i < nMaxThreads; i++) | ||||||
|  | 			workers[i].Start( | ||||||
|  | 				olc::vi2d(pix_tl.x + nSectionWidth * i, pix_tl.y),  | ||||||
|  | 				olc::vi2d(pix_tl.x + nSectionWidth * (i + 1), pix_br.y),  | ||||||
|  | 				olc::vd2d(frac_tl.x + dFractalWidth * double(i), frac_tl.y),  | ||||||
|  | 				olc::vd2d(frac_tl.x + dFractalWidth * double(i + 1), frac_br.y),  | ||||||
|  | 				iterations); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		while (nWorkerComplete < nMaxThreads) // Wait for all workers to complete
 | ||||||
|  | 		{ }			 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	bool OnUserUpdate(float fElapsedTime) override | ||||||
|  | 	{ | ||||||
|  | 
 | ||||||
|  | 		// Get mouse location this frame
 | ||||||
|  | 		olc::vd2d vMouse = { (double)GetMouseX(), (double)GetMouseY() }; | ||||||
|  | 
 | ||||||
|  | 		// Handle Pan & Zoom
 | ||||||
|  | 		if (GetMouse(2).bPressed) | ||||||
|  | 		{ | ||||||
|  | 			vStartPan = vMouse; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (GetMouse(2).bHeld) | ||||||
|  | 		{ | ||||||
|  | 			vOffset -= (vMouse - vStartPan) / vScale; | ||||||
|  | 			vStartPan = vMouse; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		olc::vd2d vMouseBeforeZoom; | ||||||
|  | 		ScreenToWorld(vMouse, vMouseBeforeZoom); | ||||||
|  | 
 | ||||||
|  | 		if (GetKey(olc::Key::Q).bHeld || GetMouseWheel() > 0) vScale *= 1.1; | ||||||
|  | 		if (GetKey(olc::Key::A).bHeld || GetMouseWheel() < 0) vScale *= 0.9; | ||||||
|  | 		 | ||||||
|  | 		olc::vd2d vMouseAfterZoom; | ||||||
|  | 		ScreenToWorld(vMouse, vMouseAfterZoom); | ||||||
|  | 		vOffset += (vMouseBeforeZoom - vMouseAfterZoom); | ||||||
|  | 		 | ||||||
|  | 		olc::vi2d pix_tl = { 0,0 }; | ||||||
|  | 		olc::vi2d pix_br = { ScreenWidth(), ScreenHeight() }; | ||||||
|  | 		olc::vd2d frac_tl = { -2.0, -1.0 }; | ||||||
|  | 		olc::vd2d frac_br = { 1.0, 1.0 }; | ||||||
|  | 
 | ||||||
|  | 		ScreenToWorld(pix_tl, frac_tl); | ||||||
|  | 		ScreenToWorld(pix_br, frac_br); | ||||||
|  | 
 | ||||||
|  | 		// Handle User Input
 | ||||||
|  | 		if (GetKey(olc::K1).bPressed) nMode = 0; | ||||||
|  | 		if (GetKey(olc::K2).bPressed) nMode = 1; | ||||||
|  | 		if (GetKey(olc::K3).bPressed) nMode = 2; | ||||||
|  | 		if (GetKey(olc::K4).bPressed) nMode = 3; | ||||||
|  | 		if (GetKey(olc::K5).bPressed) nMode = 4; | ||||||
|  | 		if (GetKey(olc::K6).bPressed) nMode = 5; | ||||||
|  | 		if (GetKey(olc::UP).bPressed) nIterations += 64; | ||||||
|  | 		if (GetKey(olc::DOWN).bPressed) nIterations -= 64; | ||||||
|  | 		if (nIterations < 64) nIterations = 64; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		// START TIMING
 | ||||||
|  | 		auto tp1 = std::chrono::high_resolution_clock::now(); | ||||||
|  | 
 | ||||||
|  | 		// Do the computation
 | ||||||
|  | 		switch (nMode) | ||||||
|  | 		{ | ||||||
|  | 		case 0: CreateFractalBasic(pix_tl, pix_br, frac_tl, frac_br, nIterations); break; | ||||||
|  | 		case 1: CreateFractalPreCalculate(pix_tl, pix_br, frac_tl, frac_br, nIterations); break; | ||||||
|  | 		case 2: CreateFractalNoComplex(pix_tl, pix_br, frac_tl, frac_br, nIterations); break; | ||||||
|  | 		case 3: CreateFractalIntrinsics(pix_tl, pix_br, frac_tl, frac_br, nIterations); break; | ||||||
|  | 		case 4: CreateFractalThreads(pix_tl, pix_br, frac_tl, frac_br, nIterations); break; | ||||||
|  | 		case 5: CreateFractalThreadPool(pix_tl, pix_br, frac_tl, frac_br, nIterations); break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// STOP TIMING
 | ||||||
|  | 		auto tp2 = std::chrono::high_resolution_clock::now(); | ||||||
|  | 		std::chrono::duration<double> elapsedTime = tp2 - tp1; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		// Render result to screen
 | ||||||
|  | 		for (int y = 0; y < ScreenHeight(); y++) | ||||||
|  | 		{ | ||||||
|  | 			for (int x = 0; x < ScreenWidth(); x++) | ||||||
|  | 			{ | ||||||
|  | 				int i = pFractal[y * ScreenWidth() + x]; | ||||||
|  | 				float n = (float)i; | ||||||
|  | 				float a = 0.1f; | ||||||
|  | 				// Thank you @Eriksonn - Wonderful Magic Fractal Oddball Man
 | ||||||
|  | 				Draw(x, y, olc::PixelF(0.5f * sin(a * n) + 0.5f, 0.5f * sin(a * n + 2.094f) + 0.5f,  0.5f * sin(a * n + 4.188f) + 0.5f)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Render UI
 | ||||||
|  | 		switch (nMode) | ||||||
|  | 		{ | ||||||
|  | 		case 0: DrawString(0, 0, "1) Naive Method", olc::WHITE, 3); break; | ||||||
|  | 		case 1: DrawString(0, 0, "2) Precalculate Method", olc::WHITE, 3); break; | ||||||
|  | 		case 2: DrawString(0, 0, "3) Hand-code Maths Method", olc::WHITE, 3); break; | ||||||
|  | 		case 3: DrawString(0, 0, "4) Vector Extensions (AVX2) Method", olc::WHITE, 3); break; | ||||||
|  | 		case 4: DrawString(0, 0, "5) Threads Method", olc::WHITE, 3); break; | ||||||
|  | 		case 5: DrawString(0, 0, "6) ThreadPool Method", olc::WHITE, 3); break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		DrawString(0, 30, "Time Taken: " + std::to_string(elapsedTime.count()) + "s", olc::WHITE, 3); | ||||||
|  | 		DrawString(0, 60, "Iterations: " + std::to_string(nIterations), olc::WHITE, 3); | ||||||
|  | 		return !(GetKey(olc::Key::ESCAPE).bPressed); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Pan & Zoom variables
 | ||||||
|  | 	olc::vd2d vOffset = { 0.0, 0.0 }; | ||||||
|  | 	olc::vd2d vStartPan = { 0.0, 0.0 }; | ||||||
|  | 	olc::vd2d vScale = { 1280.0 / 2.0, 720.0 }; | ||||||
|  | 	 | ||||||
|  | 
 | ||||||
|  | 	// Convert coordinates from World Space --> Screen Space
 | ||||||
|  | 	void WorldToScreen(const olc::vd2d& v, olc::vi2d &n) | ||||||
|  | 	{ | ||||||
|  | 		n.x = (int)((v.x - vOffset.x) * vScale.x); | ||||||
|  | 		n.y = (int)((v.y - vOffset.y) * vScale.y); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Convert coordinates from Screen Space --> World Space
 | ||||||
|  | 	void ScreenToWorld(const olc::vi2d& n, olc::vd2d& v) | ||||||
|  | 	{ | ||||||
|  | 		v.x = (double)(n.x) / vScale.x + vOffset.x; | ||||||
|  | 		v.y = (double)(n.y) / vScale.y + vOffset.y; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | std::atomic<int> olcFractalExplorer::nWorkerComplete = 0; | ||||||
|  | 
 | ||||||
|  | int main() | ||||||
|  | { | ||||||
|  | 	olcFractalExplorer demo; | ||||||
|  | 	if (demo.Construct(1280, 720, 1, 1, false, false)) | ||||||
|  | 		demo.Start(); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user