Bladeren bron

Merge branch 'master' of https://git.winfor.ch/nicolas/Almond

Nicolas Winkler 5 jaren geleden
bovenliggende
commit
e4fa0a48a0

+ 24 - 11
CMakeLists.txt

@@ -37,7 +37,7 @@ target_include_directories(Almond PUBLIC ${FFMPEG_INCLUDE_DIRS})
 target_link_libraries(Almond PUBLIC mandel asmjit qd)
 target_link_libraries(Almond PUBLIC Qt5::Core Qt5::Widgets Qt5::OpenGL Qt5::Xml)
 target_link_libraries(Almond PUBLIC ${FFMPEG_LIBRARIES})
-target_link_libraries(Almond PUBLIC OpenGL::OpenGL)
+target_link_libraries(Almond PUBLIC OpenGL::GL)
 
 if(Boost_FOUND)
     target_compile_definitions(Almond PUBLIC WITH_BOOST)
@@ -47,18 +47,31 @@ endif(Boost_FOUND)
 
 
 if(OpenMP_CXX_FOUND)
-    target_link_libraries(mandel PUBLIC OpenMP::OpenMP_CXX)
+    target_link_libraries(Almond PUBLIC OpenMP::OpenMP_CXX)
 endif()
 
-install(TARGETS Almond RUNTIME DESTINATION "bin")
-set(CPACK_GENERATOR "DEB")
-set(CPACK_SOURCE_GENERATOR "DEB")
-set(CPACK_COMPONENTS_ALL Almond)
-set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Nicolas Winkler")
-set(CPACK_DEBIAN_PACKAGE_DEPENDS "qt5-default,libavformat58,libavdevice58,libavfilter7,libavutil56,libswscale5,libgl1,ocl-icd-libopencl1")
-set(CPACK_SET_DESTDIR True)
-set(CPACK_INSTALL_PREFIX "/usr")
 
-include(CPack)
+set(CPACK_PACKAGE_NAME "Almond")
+set(CPACK_PACKAGE_VERSION "1.0.0")
+IF (WIN32)
+    set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MyPackage Installation Example")
+    include(CPack)
+    include(CPackIFW)
+    cpack_add_component(Almond
+        DISPLAY_NAME "Almond"
+        DESCRIPTION "Almond Application")
+    cpack_ifw_configure_component(Almond)
+
+ELSEIF (UNIX AND NOT APPLE)
+    install(TARGETS Almond RUNTIME DESTINATION "bin")
+    set(CPACK_GENERATOR "DEB")
+    set(CPACK_SOURCE_GENERATOR "DEB")
+    set(CPACK_COMPONENTS_ALL Almond)
+    set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Nicolas Winkler")
+    set(CPACK_DEBIAN_PACKAGE_DEPENDS "qt5-default,libavformat58,libavdevice58,libavfilter7,libavutil56,libswscale5,libgl1,ocl-icd-libopencl1")
+    set(CPACK_SET_DESTDIR True)
+    set(CPACK_INSTALL_PREFIX "/usr")
+    include(CPack)
+ENDIF()
 
 

+ 2 - 1
MandelWidget.cpp

@@ -342,7 +342,8 @@ MandelView::MandelView(mnd::MandelGenerator* generator, MandelWidget& owner) :
 }
 
 
-int MandelView::getLevel(mnd::Real dpp) {
+int MandelView::getLevel(mnd::Real dpp)
+{
     return int(mnd::log2(dpp / chunkSize));
 }
 

+ 2 - 2
libmandel/CMakeLists.txt

@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.9)
+cmake_minimum_required(VERSION 3.12)
 
 set(ARCH "X86_64" CACHE STRING "Target Architecture")
 option(AVX512 "generate code that can make use of avx-512-instructions" ON)
@@ -32,6 +32,7 @@ SET(MandelSources
     src/IterationFormula.cpp
     src/IterationCompiler.cpp
     src/IterationIR.cpp
+    src/NaiveIRGenerator.cpp
 )
 FILE(GLOB MandelHeaders include/*.h)
 
@@ -89,7 +90,6 @@ if (APPLE AND OpenCL_FOUND)
     SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -framework OpenCL")
 endif()
 
-    target_compile_definitions(mandel PUBLIC WITH_BOOST)
 if(Boost_FOUND)
     target_compile_definitions(mandel PUBLIC WITH_BOOST)
     target_include_directories(mandel PRIVATE ${Boost_INCLUDE_DIRS})

+ 0 - 15
libmandel/include/IterationGenerator.h

@@ -15,8 +15,6 @@ namespace mnd
 
     class NaiveGenerator;
 
-    template<typename T>
-    class NaiveIRGenerator;
     class CompiledGenerator;
     class CompiledGeneratorVec;
     class CompiledClGenerator;
@@ -55,19 +53,6 @@ private:
 };
 
 
-template<typename T>
-class mnd::NaiveIRGenerator : public mnd::MandelGenerator
-{
-    const ir::Formula& form;
-public:
-    NaiveIRGenerator(const ir::Formula& irf, mnd::Precision prec);
-    NaiveIRGenerator(NaiveIRGenerator&&) = default;
-
-    virtual void generate(const MandelInfo& info, float* data);
-    double calc(ir::Node* expr, double a, double b, double x, double y);
-};
-
-
 #if defined(__x86_64__) || defined(_M_X64)
 class mnd::CompiledGenerator : public mnd::MandelGenerator
 {

+ 26 - 0
libmandel/include/NaiveIRGenerator.h

@@ -0,0 +1,26 @@
+#ifndef MANDEL_NAIVEIRGENERATOR_H
+#define MANDEL_NAIVEIRGENERATOR_H
+
+#include "IterationIR.h"
+#include "Generators.h"
+
+namespace mnd
+{
+    template<typename T>
+    class NaiveIRGenerator;
+}
+
+
+template<typename T>
+class mnd::NaiveIRGenerator : public mnd::MandelGenerator
+{
+    const ir::Formula& form;
+public:
+    NaiveIRGenerator(const ir::Formula& irf, mnd::Precision prec = mnd::getType<T>());
+    NaiveIRGenerator(NaiveIRGenerator&&) = default;
+
+    virtual void generate(const MandelInfo& info, float* data);
+};
+
+
+#endif // MANDEL_NAIVEIRGENERATOR_H

+ 6 - 2
libmandel/src/CpuGenerators.cpp

@@ -67,9 +67,11 @@ void CpuGenerator<T, mnd::NONE, parallel>::generate(const mnd::MandelInfo& info,
     T juliaX = mnd::convert<T>(info.juliaX);
     T juliaY = mnd::convert<T>(info.juliaY);
 
+#if defined(_OPENMP)
     if constexpr (parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = viewy + T(double(j)) * hpp;
         for (long i = 0; i < info.bWidth; i++) {
@@ -215,9 +217,11 @@ void CpuGenerator<mnd::MpfrFloat<bits>, mnd::NONE, parallel>::generate(const mnd
     const MandelViewport& view = info.view;
     using T = mnd::MpfrFloat<bits>;
 
+#if defined(_OPENMP)
     if constexpr (parallel)
         omp_set_num_threads(2 * omp_get_num_procs());
-#pragma omp parallel for if (parallel)
+#   pragma omp parallel for if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y) + T(j) * T(view.height / info.bHeight);
         long i = 0;

+ 9 - 3
libmandel/src/CpuGeneratorsAVX.cpp

@@ -37,9 +37,11 @@ void CpuGenerator<float, mnd::X86_AVX, parallel>::generate(const mnd::MandelInfo
     __m256 juliaX = { jX, jX, jX, jX, jX, jX, jX, jX };
     __m256 juliaY = { jY, jY, jY, jY, jY, jY, jY, jY };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y) + T(j) * T(view.height / info.bHeight);
         __m256 ys = _mm256_set1_ps(y);
@@ -172,9 +174,11 @@ void CpuGenerator<double, mnd::X86_AVX, parallel>::generate(const mnd::MandelInf
     __m256d juliaX = { jX, jX, jX, jX };
     __m256d juliaY = { jY, jY, jY, jY };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y + T(j) * view.height / info.bHeight);
         __m256d ys = { y, y, y, y };
@@ -430,9 +434,11 @@ void CpuGenerator<mnd::DoubleDouble, mnd::X86_AVX, parallel>::generate(const mnd
     AvxDoubleDouble juliaX = { jX[0], jX[1] };
     AvxDoubleDouble juliaY = { jY[0], jY[1] };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = viewy + T(double(j)) * hpp;
         AvxDoubleDouble ys{ y[0], y[1] };

+ 5 - 1
libmandel/src/CpuGeneratorsAVX512.cpp

@@ -29,9 +29,11 @@ void CpuGenerator<float, mnd::X86_AVX_512, parallel>::generate(const mnd::Mandel
     __m512 enumerate = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
     __m512 two = _mm512_set1_ps(2);
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
 #pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y + double(j) * view.height / info.bHeight);
         __m512 ys = _mm512_set1_ps(y);
@@ -171,9 +173,11 @@ void CpuGenerator<double, mnd::X86_AVX_512, parallel>::generate(const mnd::Mande
     __m512d viewx = { viewxf, viewxf, viewxf, viewxf, viewxf, viewxf, viewxf, viewxf };
     __m512d dpp = { dppf, dppf, dppf, dppf, dppf, dppf, dppf, dppf };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y + double(j) * view.height / info.bHeight);
         __m512d ys = { y, y, y, y, y, y, y, y };

+ 10 - 3
libmandel/src/CpuGeneratorsAVXFMA.cpp

@@ -37,9 +37,11 @@ void CpuGenerator<float, mnd::X86_AVX_FMA, parallel>::generate(const mnd::Mandel
     __m256 juliaX = { jX, jX, jX, jX, jX, jX, jX, jX };
     __m256 juliaY = { jY, jY, jY, jY, jY, jY, jY, jY };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y) + T(j) * T(view.height / info.bHeight);
         __m256 ys = {y, y, y, y, y, y, y, y};
@@ -204,9 +206,12 @@ void CpuGenerator<double, mnd::X86_AVX_FMA, parallel>::generate(const mnd::Mande
     __m256d juliaX = { jX, jX, jX, jX };
     __m256d juliaY = { jY, jY, jY, jY };
 
+
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y + T(j) * view.height / info.bHeight);
         __m256d ys = { y, y, y, y };
@@ -418,9 +423,11 @@ void CpuGenerator<mnd::DoubleDouble, mnd::X86_AVX_FMA, parallel>::generate(const
     AvxDoubleDouble juliaX = { jX[0], jX[1] };
     AvxDoubleDouble juliaY = { jY[0], jY[1] };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = viewy + T(double(j)) * hpp;
         __m256d y0s = { y.x[0], y.x[0], y.x[0], y.x[0] };

+ 6 - 2
libmandel/src/CpuGeneratorsSSE2.cpp

@@ -32,9 +32,11 @@ void CpuGenerator<float, mnd::X86_SSE2, parallel>::generate(const mnd::MandelInf
     __m128 juliaX = { jX, jX, jX, jX };
     __m128 juliaY = { jY, jY, jY, jY };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y) + T(j) * T(view.height / info.bHeight);
         __m128 ys = {y, y, y, y};
@@ -137,9 +139,11 @@ void CpuGenerator<double, mnd::X86_SSE2, parallel>::generate(const mnd::MandelIn
     __m128d juliaX = { jX, jX };
     __m128d juliaY = { jY, jY };
 
+#if defined(_OPENMP)
     if constexpr(parallel)
         omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = T(view.y) + T(j) * T(view.height / info.bHeight);
         __m128d ys = { y, y };

+ 4 - 0
libmandel/src/IterationCompiler.cpp

@@ -1,4 +1,5 @@
 #include "IterationCompiler.h"
+#include "NaiveIRGenerator.h"
 
 #include "ExecData.h"
 #include "Mandel.h"
@@ -783,6 +784,9 @@ namespace mnd
             printf("asm avxvec: %s\n", dgavx->dump().c_str()); fflush(stdout);
             vec.push_back(std::move(dgavx));
         }
+
+        //vec.push_back(std::make_unique<NaiveIRGenerator<mnd::DoubleDouble>>(irf));
+        //vec.push_back(std::make_unique<NaiveIRGenerator<mnd::QuadDouble>>(irf));
         
         //auto dg = std::make_unique<NaiveIRGenerator>(*irf, mnd::getPrecision<double>());
 

+ 10 - 155
libmandel/src/IterationGenerator.cpp

@@ -9,19 +9,9 @@
 
 using mnd::IterationGenerator;
 using mnd::NaiveGenerator;
-using mnd::NaiveIRGenerator;
 using mnd::IterationFormula;
 
 
-namespace mnd
-{
-    template class NaiveIRGenerator<float>;
-    template class NaiveIRGenerator<double>;
-    template class NaiveIRGenerator<mnd::DoubleDouble>;
-    template class NaiveIRGenerator<mnd::QuadDouble>;
-}
-
-
 IterationGenerator::IterationGenerator(IterationFormula z0, IterationFormula zi,
             mnd::Precision prec, mnd::CpuExtension ex) :
     mnd::MandelGenerator{ prec, ex },
@@ -51,9 +41,11 @@ void NaiveGenerator::generate(const mnd::MandelInfo& info, float* data)
     T wpp = mnd::convert<T>(view.width / info.bWidth);
     T hpp = mnd::convert<T>(view.height / info.bHeight);
 
-    if constexpr (parallel)
+#if defined(_OPENMP)
+   if constexpr (parallel)
         omp_set_num_threads(omp_get_num_procs());
-//#pragma omp parallel for schedule(static, 1) if (parallel)
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
     for (long j = 0; j < info.bHeight; j++) {
         T y = viewy + T(double(j)) * hpp;
         long i = 0;
@@ -133,147 +125,6 @@ std::complex<double> NaiveGenerator::calc(mnd::Expression& expr, std::complex<do
     return result;
 }
 
-
-template<typename T>
-NaiveIRGenerator<T>::NaiveIRGenerator(const mnd::ir::Formula& irf,
-            mnd::Precision prec) :
-    mnd::MandelGenerator{ prec },
-    form{ irf }
-{
-}
-
-
-template<typename U>
-void NaiveIRGenerator<U>::generate(const mnd::MandelInfo& info, float* data)
-{
-    const MandelViewport& view = info.view;
-
-    const bool parallel = true;
-    using T = double;
-    T viewx = mnd::convert<T>(view.x);
-    T viewy = mnd::convert<T>(view.y);
-    T wpp = mnd::convert<T>(view.width / info.bWidth);
-    T hpp = mnd::convert<T>(view.height / info.bHeight);
-
-    if constexpr (parallel)
-        omp_set_num_threads(omp_get_num_procs());
-//#pragma omp parallel for schedule(static, 1) if (parallel)
-    for (long j = 0; j < info.bHeight; j++) {
-        T y = viewy + T(double(j)) * hpp;
-        long i = 0;
-        for (i; i < info.bWidth; i++) {
-            T x = viewx + T(double(i)) * wpp;
-
-            T a = calc(form.startA, x, y, 0, 0);
-            T b = calc(form.startB, x, y, 0, 0);
-
-            int k = 0;
-            for (k = 0; k < info.maxIter; k++) {
-                double newA = calc(form.newA, a, b, x, y);
-                double newB = calc(form.newB, a, b, x, y);
-                a = newA;
-                b = newB;
-                if (a * a + b * b >= 16.0)
-                    break;
-            }
-            data[i + j * info.bWidth] = float(k);
-        }
-    }
-}
-
-
-template<typename T>
-struct EvalNode
-{
-
-};
-
-
-template<typename T>
-double NaiveIRGenerator<T>::calc(mnd::ir::Node* expr, double a, double b, double x, double y)
-{
-    struct DoubleVisitor
-    {
-        double a, b, x, y;
-        double visitNode(ir::Node* n) {
-            auto& nodeData = getNodeData(n);
-            //if (std::get_a)
-            return std::visit(*this, *n);
-        }
-
-        const std::any& getNodeData(ir::Node* n) {
-            return std::visit([](auto& n) {
-                return n.nodeData;
-            }, *n);
-        }
-
-        double operator()(const ir::Constant& c) {
-            return mnd::convert<double>(c.value);
-        }
-
-        double operator()(const ir::Variable& v) {
-            if (v.name == "z_re")
-                return a;
-            else if (v.name == "z_im")
-                return b;
-            else if (v.name == "c_re")
-                return x;
-            else if (v.name == "c_im")
-                return y;
-            else
-                return 0.0;
-        }
-
-        double operator()(const ir::Negation& n) {
-            return -visitNode(n.value);
-        }
-
-        double operator()(const ir::Addition& n) {
-            return visitNode(n.left) + visitNode(n.right);
-        }
-
-        double operator()(const ir::Subtraction& n) {
-            return visitNode(n.left) - visitNode(n.right);
-        }
-
-        double operator()(const ir::Multiplication& n) {
-            return visitNode(n.left) * visitNode(n.right);
-        }
-
-        double operator()(const ir::Division& n) {
-            return visitNode(n.left) / visitNode(n.right);
-        }
-
-        double operator()(const ir::Atan2& n) {
-            return ::atan2(visitNode(n.left), visitNode(n.right));
-        }
-
-        double operator()(const ir::Pow& n) {
-            return ::pow(visitNode(n.left), visitNode(n.right));
-        }
-
-        double operator()(const ir::Cos& n) {
-            return ::cos(visitNode(n.value));
-        }
-
-        double operator()(const ir::Sin& n) {
-            return ::sin(visitNode(n.value));
-        }
-
-        double operator()(const ir::Exp& n) {
-            return ::exp(visitNode(n.value));
-        }
-
-        double operator()(const ir::Ln& n) {
-            return ::log(visitNode(n.value));
-        }
-    };
-
-    DoubleVisitor dv{ a, b, x, y };
-    return dv.visitNode(expr);
-}
-
-
 using mnd::CompiledGenerator;
 using mnd::CompiledGeneratorVec;
 using mnd::CompiledClGenerator;
@@ -323,8 +174,10 @@ void CompiledGenerator::generate(const mnd::MandelInfo& info, float* data)
 {
     using IterFunc = int (*)(double, double, int);
 
+#if defined(_OPENMP)
     omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1)
+#   pragma omp parallel for schedule(static, 1)
+#endif
     for (int i = 0; i < info.bHeight; i++) {
         double y = mnd::convert<double>(info.view.y + info.view.height * i / info.bHeight);
         for (int j = 0; j < info.bWidth; j++) {
@@ -365,8 +218,10 @@ void CompiledGeneratorVec::generate(const mnd::MandelInfo& info, float* data)
 
     double dx = mnd::convert<double>(info.view.width / info.bWidth);
 
+#if defined(_OPENMP)
     omp_set_num_threads(omp_get_num_procs());
-#pragma omp parallel for schedule(static, 1)
+#   pragma omp parallel for schedule(static, 1)
+#endif
     for (int i = 0; i < info.bHeight; i++) {
         double y = mnd::convert<double>(info.view.y + info.view.height * i / info.bHeight);
         for (int j = 0; j < info.bWidth; j += 8) {

+ 172 - 0
libmandel/src/NaiveIRGenerator.cpp

@@ -0,0 +1,172 @@
+#include "NaiveIRGenerator.h"
+#include <omp.h>
+
+
+using mnd::NaiveIRGenerator;
+
+
+namespace mnd
+{
+    template class NaiveIRGenerator<float>;
+    template class NaiveIRGenerator<double>;
+    template class NaiveIRGenerator<mnd::DoubleDouble>;
+    template class NaiveIRGenerator<mnd::QuadDouble>;
+}
+
+
+template<typename T>
+NaiveIRGenerator<T>::NaiveIRGenerator(const mnd::ir::Formula& irf,
+    mnd::Precision prec) :
+    mnd::MandelGenerator{ prec },
+    form{ irf }
+{
+}
+
+
+template<typename T>
+struct EvalRes
+{
+    size_t incVal;
+    T result;
+};
+
+
+using namespace mnd;
+template<typename T>
+struct TVisitor
+{
+    T a, b, x, y;
+    size_t incrementValue;
+
+    T visitNode(ir::Node* n) {
+        EvalRes<T>* nodeData = getNodeData(n);
+        if (nodeData) {
+            if (nodeData->incVal == incrementValue)
+                return nodeData->result;
+        }
+        T res = std::visit(*this, *n);
+        if (nodeData) {
+            nodeData->incVal = incrementValue;
+            nodeData->result = res;
+        }
+        return res;
+    }
+
+    EvalRes<T>* getNodeData(ir::Node* n) {
+        assert(n != nullptr);
+        std::any& x = std::visit([](auto& n) -> std::any& {
+            return n.nodeData;
+        }, *n);
+        if (auto* v = std::any_cast<EvalRes<T>>(&x))
+            return v;
+        else
+            return nullptr;
+    }
+
+    T operator()(const ir::Constant& c) {
+        return mnd::convert<double>(c.value);
+    }
+
+    T operator()(const ir::Variable& v) {
+        if (v.name == "z_re")
+            return a;
+        else if (v.name == "z_im")
+            return b;
+        else if (v.name == "c_re")
+            return x;
+        else if (v.name == "c_im")
+            return y;
+        else
+            return 0.0;
+    }
+
+    T operator()(const ir::Negation& n) {
+        return -visitNode(n.value);
+    }
+
+    T operator()(const ir::Addition& n) {
+        return visitNode(n.left) + visitNode(n.right);
+    }
+
+    T operator()(const ir::Subtraction& n) {
+        return visitNode(n.left) - visitNode(n.right);
+    }
+
+    T operator()(const ir::Multiplication& n) {
+        return visitNode(n.left) * visitNode(n.right);
+    }
+
+    T operator()(const ir::Division& n) {
+        return visitNode(n.left) / visitNode(n.right);
+    }
+
+    T operator()(const ir::Atan2& n) {
+        return ::atan2(visitNode(n.left), visitNode(n.right));
+    }
+
+    T operator()(const ir::Pow& n) {
+        return ::pow(visitNode(n.left), visitNode(n.right));
+    }
+
+    T operator()(const ir::Cos& n) {
+        return ::cos(visitNode(n.value));
+    }
+
+    T operator()(const ir::Sin& n) {
+        return ::sin(visitNode(n.value));
+    }
+
+    T operator()(const ir::Exp& n) {
+        return ::exp(visitNode(n.value));
+    }
+
+    T operator()(const ir::Ln& n) {
+        return ::log(visitNode(n.value));
+    }
+};
+
+
+template<typename T>
+void NaiveIRGenerator<T>::generate(const mnd::MandelInfo& info, float* data)
+{
+    const MandelViewport& view = info.view;
+
+    const bool parallel = true;
+    T viewx = mnd::convert<T>(view.x);
+    T viewy = mnd::convert<T>(view.y);
+    T wpp = mnd::convert<T>(view.width / info.bWidth);
+    T hpp = mnd::convert<T>(view.height / info.bHeight);
+
+#if defined(_OPENMP)
+    if constexpr (parallel)
+        omp_set_num_threads(omp_get_num_procs());
+#   pragma omp parallel for schedule(static, 1) if (parallel)
+#endif
+    for (long j = 0; j < info.bHeight; j++) {
+        T y = viewy + T(double(j)) * hpp;
+        long i = 0;
+        for (i; i < info.bWidth; i++) {
+            T x = viewx + T(double(i)) * wpp;
+
+            TVisitor<T> beforeVisitor{ 0, 0, x, y, 0 };
+
+            T a = beforeVisitor.visitNode(form.startA);
+            T b = beforeVisitor.visitNode(form.startB);
+
+            TVisitor<T> visitor{ a, b, x, y, 0 };
+            int k = 0;
+            for (k = 0; k < info.maxIter; k++) {
+                T newA = visitor.visitNode(form.newA);
+                T newB = visitor.visitNode(form.newB);
+                a = newA;
+                b = newB;
+                if (a * a + b * b >= 16.0)
+                    break;
+                visitor.incrementValue++;
+            }
+            data[i + j * info.bWidth] = float(k);
+        }
+    }
+}
+
+