parent
64c3ec7f27
commit
deaadfef4a
@ -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…
Reference in new issue