Browse Source

initial commit

Nicolas Winkler 6 years ago
commit
ed5d44985a
26 changed files with 2333 additions and 0 deletions
  1. 94 0
      Almond.cpp
  2. 45 0
      Almond.h
  3. 114 0
      Almond.pro
  4. 4 0
      Almond.qrc
  5. 70 0
      Almond.ui
  6. 83 0
      Bitmap.cpp
  7. 65 0
      Bitmap.h
  8. 10 0
      Color.h
  9. 520 0
      Fixed.h
  10. 217 0
      Generators.cpp
  11. 184 0
      Generators.h
  12. 24 0
      GenericMandelbrot.h
  13. 207 0
      MandelWidget.cpp
  14. 57 0
      MandelWidget.h
  15. 30 0
      QueueManager.cpp
  16. 68 0
      QueueManager.h
  17. 19 0
      SectionManager.cpp
  18. 15 0
      SectionManager.h
  19. 146 0
      VideoStream.cpp
  20. 39 0
      VideoStream.h
  21. 55 0
      benchmarkdialog.cpp
  22. 29 0
      benchmarkdialog.h
  23. 94 0
      benchmarks.ui
  24. 132 0
      exportimagedialog.ui
  25. 2 0
      fdgsdfg.qrc
  26. 10 0
      main.cpp

+ 94 - 0
Almond.cpp

@@ -0,0 +1,94 @@
+#include "Almond.h"
+#include <QIntValidator>
+#include <QFileDialog>
+#include <QMessageBox>
+#include "benchmarkdialog.h"
+
+Almond::Almond(QWidget *parent) :
+    QMainWindow(parent)
+{
+    ui.setupUi(this);
+    printf("not yet created!\n");
+    mw = std::make_unique<MandelWidget>(ui.centralWidget);
+    printf("created!\n");
+    ui.verticalLayout_left->addWidget(mw.get());
+    //ui.verticalLayout_left->addWidget(new MyGLWidget(ui.centralWidget));
+    //mw->show();
+}
+
+void Almond::on_pushButton_clicked()
+{
+    ExportImageDialog dialog(this);
+    auto response = dialog.exec();
+    if (response == 1) {
+        MandelInfo mi;
+        mi.maxIter = dialog.getMaxIterations();
+        mi.view = mw->getViewport();
+        mi.bWidth = dialog.getWidth();
+        mi.bHeight = dialog.getHeight();
+        mi.view.adjustAspectRatio(mi.bWidth, mi.bHeight);
+        CpuGenerator<double> cpg;
+        auto bitmap = cpg.generate(mi);
+        QImage img((unsigned char*)bitmap.pixels.get(), bitmap.width, bitmap.height, bitmap.width * 3, QImage::Format_RGB888);
+        img.save(dialog.getPath());
+    }
+}
+
+
+ExportImageDialog::ExportImageDialog(QWidget* parent) :
+    QDialog{ parent }
+{
+    eid.setupUi(this);
+    eid.maxIterations->setValidator(new QIntValidator(1, 10000000, this));
+    eid.imgWidth->setValidator(new QIntValidator(1, 10000000, this));
+    eid.imgHeight->setValidator(new QIntValidator(1, 10000000, this));
+}
+
+
+int ExportImageDialog::getMaxIterations(void) const
+{
+    return std::stoi(eid.maxIterations->text().toStdString());
+}
+
+
+int ExportImageDialog::getWidth(void) const
+{
+    return std::stoi(eid.imgWidth->text().toStdString());
+}
+
+
+int ExportImageDialog::getHeight(void) const
+{
+    return std::stoi(eid.imgHeight->text().toStdString());
+}
+
+
+QString ExportImageDialog::getPath(void) const
+{
+    return eid.savePath->text();
+}
+
+void ExportImageDialog::on_pushButton_clicked()
+{
+    QString saveAs = QFileDialog::getSaveFileName(this,
+            tr("Save exported image"), "",
+            tr("PNG image (*.png);;JPEG image (*.jpg);;All Files (*)"));
+    eid.savePath->setText(saveAs);
+    this->repaint();
+}
+
+void ExportImageDialog::on_buttonBox_accepted()
+{
+    if (eid.savePath->text() == "") {
+        QMessageBox msgBox;
+        msgBox.setText("Please specify a path.");
+        msgBox.exec();
+        reject();
+    }
+}
+
+void Almond::on_pushButton_2_clicked()
+{
+    BenchmarkDialog bd(this);
+    bd.exec();
+}

+ 45 - 0
Almond.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QDialog>
+#include "ui_Almond.h"
+#include "ui_exportimagedialog.h"
+
+#include "MandelWidget.h"
+
+#include <memory>
+
+class Almond : public QMainWindow
+{
+    Q_OBJECT
+private:
+    std::unique_ptr<MandelWidget> mw;
+public:
+    Almond(QWidget *parent = Q_NULLPTR);
+
+private slots:
+    void on_pushButton_clicked();
+
+    void on_pushButton_2_clicked();
+
+private:
+    Ui::AlmondClass ui;
+};
+
+
+class ExportImageDialog : public QDialog
+{
+    Q_OBJECT
+private:
+    Ui::ExportImageDialog eid;
+public:
+    ExportImageDialog(QWidget* parent);
+
+    int getMaxIterations(void) const;
+    int getWidth(void) const;
+    int getHeight(void) const;
+    QString getPath(void) const;
+private slots:
+    void on_pushButton_clicked();
+    void on_buttonBox_accepted();
+};

+ 114 - 0
Almond.pro

@@ -0,0 +1,114 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2019-05-03T18:16:23
+#
+#-------------------------------------------------
+
+QT       += core gui opengl
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = Almond
+TEMPLATE = app
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which has been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+CONFIG += c++17
+
+SOURCES += \
+        Almond.cpp \
+        Bitmap.cpp \
+        Generators.cpp \
+        MandelWidget.cpp \
+        QueueManager.cpp \
+        SectionManager.cpp \
+        VideoStream.cpp \
+        benchmarkdialog.cpp \
+        main.cpp
+
+HEADERS += \
+        Almond.h \
+        Bitmap.h \
+        Color.h \
+        Fixed.h \
+        Generators.h \
+        GenericMandelbrot.h \
+        MandelWidget.h \
+        QueueManager.h \
+        SectionManager.h \
+        VideoStream.h \
+        benchmarkdialog.h
+
+FORMS += \
+        Almond.ui \
+        benchmarks.ui \
+        exportimagedialog.ui
+
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+LIBS += -lopengl32
+win32:QMAKE_CXXFLAGS+= -openmp
+else:unix:QMAKE_CXXFLAGS+= -fopenmp
+win32:QMAKE_LFLAGS +=  -openmp
+else:unix:QMAKE_LFLAGS+= -fopenmp
+LIBS += -fopenmp
+
+QMAKE_CXXFLAGS += -mavx
+
+win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavcodec
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavcodec
+else:unix: LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavcodec
+
+INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+
+
+win32:CONFIG(release, debug|release): LIBS += -L$$PWD/'../../../../../Program Files (x86)/AMD APP SDK/3.0/lib/x86/' -lOpenCL
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/'../../../../../Program Files (x86)/AMD APP SDK/3.0/lib/x86/' -lOpenCL
+else:unix: LIBS += -L$$PWD/'../../../../../Program Files (x86)/AMD APP SDK/3.0/lib/x86/' -lOpenCL
+
+INCLUDEPATH += $$PWD/'../../../../../Program Files (x86)/AMD APP SDK/3.0/include'
+DEPENDPATH += $$PWD/'../../../../../Program Files (x86)/AMD APP SDK/3.0/include'
+
+win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavformat
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavformat
+else:unix: LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavformat
+
+INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+
+unix|win32: LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavdevice
+
+INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+
+unix|win32: LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavfilter
+
+INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+
+unix|win32: LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lavutil
+
+INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+
+unix|win32: LIBS += -L$$PWD/../libs/ffmpeg-4.1.1-win32-dev/lib/ -lswscale
+
+INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
+
+RESOURCES += \
+    Almond.qrc

+ 4 - 0
Almond.qrc

@@ -0,0 +1,4 @@
+<RCC>
+    <qresource prefix="Almond">
+    </qresource>
+</RCC>

+ 70 - 0
Almond.ui

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AlmondClass</class>
+ <widget class="QMainWindow" name="AlmondClass">
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>712</width>
+    <height>268</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Almond</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="2,0">
+      <item>
+       <layout class="QVBoxLayout" name="verticalLayout_left"/>
+      </item>
+      <item>
+       <layout class="QVBoxLayout" name="verticalLayout_right">
+        <item>
+         <widget class="QPushButton" name="pushButton_2">
+          <property name="text">
+           <string>Run Benchmark</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="pushButton">
+          <property name="text">
+           <string>Export Image</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <action name="actionExit">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 83 - 0
Bitmap.cpp

@@ -0,0 +1,83 @@
+#include "Bitmap.h"
+#include <cstring>
+#ifdef _WIN32
+#include <Windows.h>
+#else
+// for Linux platform, plz make sure the size of data type is correct for BMP spec.
+// if you use this on Windows or other platforms, plz pay attention to this.
+typedef int LONG;
+typedef unsigned char BYTE;
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+// __attribute__((packed)) on non-Intel arch may cause some unexpected error, plz be informed.
+
+typedef struct tagBITMAPFILEHEADER
+{
+    WORD    bfType; // 2  /* Magic identifier */
+    DWORD   bfSize; // 4  /* File size in bytes */
+    WORD    bfReserved1; // 2
+    WORD    bfReserved2; // 2
+    DWORD   bfOffBits; // 4 /* Offset to image data, bytes */ 
+} __attribute__((packed)) BITMAPFILEHEADER;
+
+typedef struct tagBITMAPINFOHEADER
+{
+    DWORD    biSize; // 4 /* Header size in bytes */
+    LONG     biWidth; // 4 /* Width of image */
+    LONG     biHeight; // 4 /* Height of image */
+    WORD     biPlanes; // 2 /* Number of colour planes */
+    WORD     biBitCount; // 2 /* Bits per pixel */
+    DWORD    biCompression; // 4 /* Compression type */
+    DWORD    biSizeImage; // 4 /* Image size in bytes */
+    LONG     biXPelsPerMeter; // 4
+    LONG     biYPelsPerMeter; // 4 /* Pixels per meter */
+    DWORD    biClrUsed; // 4 /* Number of colours */ 
+    DWORD    biClrImportant; // 4 /* Important colours */ 
+} __attribute__((packed)) BITMAPINFOHEADER;
+#endif
+#include <cstdlib>
+
+template struct Bitmap<RGBColor>;
+
+
+
+template<>
+template<>
+void Bitmap<RGBColor>::createPng<RGBColor>(const std::string& path) const
+{
+    std::FILE* out = std::fopen(path.c_str(), "wb");
+
+    BITMAPFILEHEADER bmfh;
+    bmfh.bfType = 0x4d42;
+    bmfh.bfSize = 14 + 40 + (width * height * 3);
+    bmfh.bfReserved1 = 0;
+    bmfh.bfReserved2 = 0;
+    bmfh.bfOffBits = 14 + 40;
+
+    BITMAPINFOHEADER bmih;
+    memset(&bmih, 0, 40);
+    bmih.biSize = 40;
+    bmih.biWidth = width;
+    bmih.biHeight = -long(height);
+    bmih.biPlanes = 1;
+    bmih.biBitCount = 24;
+    bmih.biCompression = 0;
+
+    std::fwrite(&bmfh, sizeof bmfh, 1, out);
+    std::fwrite(&bmih, sizeof bmih, 1, out);
+
+    size_t lineLength = (width * 3 + 3) & ~3;
+
+    std::unique_ptr<unsigned char[]> linebuf = std::make_unique<unsigned char[]>(lineLength);
+    memset(linebuf.get(), 0, lineLength);
+    for (long i = 0; i < height; i++) {
+        for (long j = 0; j < width; j++) {
+            linebuf[j * 3 + 0] = pixels[i * height + j].b;
+            linebuf[j * 3 + 1] = pixels[i * height + j].g;
+            linebuf[j * 3 + 2] = pixels[i * height + j].r;
+        }
+        std::fwrite(linebuf.get(), 1, lineLength, out);
+    }
+    std::fclose(out);
+}

+ 65 - 0
Bitmap.h

@@ -0,0 +1,65 @@
+#pragma once
+#ifndef BITMAP_H_
+#define BITMAP_H_
+
+#include "Color.h"
+#include <memory>
+#include <string>
+#include <functional>
+
+template<typename Pixel>
+struct Bitmap
+{
+    long width, height;
+    std::unique_ptr<Pixel[]> pixels;
+public:
+    Bitmap(void) :
+        width{ 0 },
+        height{ 0 },
+        pixels{ 0 }
+    {
+    }
+
+
+    Bitmap(long width, long height) :
+        width{ width }, height{ height },
+        pixels{ std::make_unique<Pixel[]>(width * height) }
+    {
+    }
+
+    Bitmap(Bitmap&&) = default;
+    Bitmap& operator = (Bitmap&&) = default;
+
+    ~Bitmap() = default;
+
+    template<typename P = Pixel>
+    auto createPng(const std::string& path) const -> typename std::enable_if<std::is_same<P, RGBColor>::value>::type;
+
+    template<typename T>
+    Bitmap<T> map(std::function<T(Pixel)> f) const {
+        Bitmap<T> b{ width, height };
+        for (::size_t i = 0; i < width * height; i++) {
+            b.pixels[i] = f(pixels[i]);
+        }
+        return b;
+    }
+
+    Pixel& get(long x, long y)
+    {
+        return pixels[x + y * width];
+    }
+
+    const Pixel& get(long x, long y) const
+    {
+        return pixels[x + y * width];
+    }
+
+    void print(void)
+    {
+        for (size_t i = 0; i < width * height; i++) {
+            printf("%03d ", int(pixels[i].r));
+        }
+    }
+};
+
+#endif // BITMAP_H_

+ 10 - 0
Color.h

@@ -0,0 +1,10 @@
+#pragma once
+#ifndef COLOR_H_
+#define COLOR_H_
+
+struct RGBColor
+{
+    unsigned char r, g, b;
+};
+
+#endif // COLOR_H_

+ 520 - 0
Fixed.h

@@ -0,0 +1,520 @@
+#pragma once
+
+#include <cinttypes>
+
+struct Fixed128
+{
+    uint64_t upper;
+    uint64_t lower;
+
+    Fixed128(const Fixed128&) = default;
+    ~Fixed128() = default;
+
+
+    inline Fixed128(uint64_t upper, uint64_t lower) :
+        upper{ upper }, lower{ lower }
+    {
+    }
+
+    inline Fixed128(uint32_t a, uint32_t b, uint32_t c, uint32_t d) :
+        upper{ (uint64_t(a) << 32) | b }, lower{ (uint64_t(c) << 32) | d }
+    {
+    }
+
+    inline Fixed128(double x)
+    {
+        const double twoToThe32 = double(0x100000000ULL);
+        upper = uint64_t(int64_t(x * twoToThe32));
+        double remainder = x - double(upper) / twoToThe32;
+        lower = uint64_t(int64_t(x * twoToThe32 * twoToThe32 * twoToThe32));
+        /*int integerPart = ::floor(x);
+        double fractionalPart = x - integerPart;
+        upper = int64_t(integerPart) << 32;
+        upper |= uint64_t(fractionalPart * (1ULL << 32)) & 0xFFFFFFFFULL;
+        lower = 0;// uint64_t(fractionalPart * (1ULL << 32) * (1ULL << 63) * 2);*/
+    }
+
+    inline Fixed128 operator + (const Fixed128& other) const {
+        uint64_t lowerAdded = lower + other.lower;
+        uint64_t upperAdded = upper + other.upper + (lowerAdded < lower);
+        return Fixed128{ upperAdded, lowerAdded };
+    }
+    
+    inline Fixed128& operator +=(const Fixed128& other) {
+        uint64_t lowerAdded = lower + other.lower;
+        upper += other.upper + (lowerAdded < lower);
+        lower = lowerAdded;
+        return *this;
+    }
+
+    inline Fixed128 operator - (const Fixed128& other) const {
+        uint64_t lowerSubbed = lower - other.lower;
+        uint64_t upperSubbed = upper - other.upper - (lowerSubbed > lower);
+        return Fixed128{ upperSubbed, lowerSubbed };
+    }
+
+    inline Fixed128 operator - (void) const {
+        return this->operator~() + Fixed128{ 0, 0, 0, 1 };
+    }
+
+//private:
+    static inline std::pair<uint64_t, uint64_t> mul64(int64_t a, int64_t b) {
+        int32_t aa[2] = { a >> 32, a & 0xFFFFFFFF };
+        int32_t bb[2] = { b >> 32, b & 0xFFFFFFFF };
+
+        int32_t res[4];
+        int64_t temp = int64_t(aa[1]) * bb[1];
+        res[3] = temp & 0xFFFFFFFF;
+        int32_t carry = temp >> 32;
+        temp = int64_t(aa[0]) * bb[1] + int64_t(aa[1]) * bb[0] + carry;
+        res[2] = temp & 0xFFFFFFFF;
+        carry = temp >> 32;
+        temp = int64_t(aa[0]) * bb[0] + carry;
+        res[1] = temp & 0xFFFFFFFF;
+        res[0] = temp >> 32;
+
+        return std::make_pair(uint64_t((int64_t(res[0]) << 32) | res[1]), uint64_t((int64_t(res[2]) << 32) | res[3]));
+    }
+
+    static inline std::pair<uint64_t, uint64_t> mulu64(uint64_t a, uint64_t b) {
+        uint32_t aa[2] = { a >> 32, a & 0xFFFFFFFF };
+        uint32_t bb[2] = { b >> 32, b & 0xFFFFFFFF };
+
+        uint32_t res[4];
+        uint64_t temp = uint64_t(aa[1]) * bb[1];
+        res[3] = temp & 0xFFFFFFFF;
+        uint32_t carry = temp >> 32;
+        temp = uint64_t(aa[0]) * bb[1] + uint64_t(aa[1]) * bb[0] + carry;
+        res[2] = temp & 0xFFFFFFFF;
+        carry = temp >> 32;
+        temp = uint64_t(aa[0]) * bb[0] + carry;
+        res[1] = temp & 0xFFFFFFFF;
+        res[0] = temp >> 32;
+
+        return std::make_pair((uint64_t(res[0]) << 32) | res[1], (uint64_t(res[2]) << 32) | res[3] );
+    }
+
+public:
+    inline Fixed128 operator * (const Fixed128& other) const {
+        if (isNegative()) {
+            return -(other * this->operator-());
+        }
+        if (other.isNegative()) {
+            return -(*this * (-other));
+        }
+        auto [uuc, uu] = mulu64(upper, other.upper);
+        auto [ulc, ul] = mulu64(upper, other.lower);
+        auto [luc, lu] = mulu64(lower, other.upper);
+        auto [llc, ll] = mulu64(lower, other.lower);
+
+        uint64_t res[4] = { 0, 0, 0, 0 };
+        res[3] = ll;
+        res[2] += lu;
+        res[2] += ul;
+        if (res[2] < ul)
+            res[1]++;
+        res[2] += llc;
+        if (res[2] < llc)
+            res[1]++;
+        res[1] += uu;
+        if (res[1] < uu)
+            res[0]++;
+        res[1] += ulc;
+        if (res[1] < ulc)
+            res[0]++;
+        res[1] += luc;
+        if (res[1] < luc)
+            res[0]++;
+        res[0] += uuc;
+
+
+        return Fixed128{ uint32_t(res[0] & 0xFFFFFFFF), uint32_t(int64_t(res[1]) >> 32), uint32_t(res[1] & 0xFFFFFFFF), uint32_t(int64_t(res[2]) >> 32) };
+
+        /*if (isNegative()) {
+            return -(this->operator-() * other);
+        }
+        if (other.isNegative()) {
+            return -(*this * (-other));
+        }
+
+        bool otherNegative = other.isNegative();
+
+        uint32_t quarters[4] = {
+            (upper >> 32) & 0xFFFFFFFF,
+            upper & 0xFFFFFFFF,
+            (lower >> 32) & 0xFFFFFFFF,
+            lower & 0xFFFFFFFF
+        };
+
+        auto [a, ra] = other.mul(quarters[0]);
+        auto [b, rb] = other.mul(quarters[1]);
+        auto [c, rc] = other.mul(quarters[2]);
+        auto [d, rd] = other.mul(quarters[3]);
+        b.arshift(1);
+        c.arshift(2);
+        d.arshift(3);
+        Fixed128 carries = { uint32_t(rb), uint32_t(rc), uint32_t(rd), 0 };
+        Fixed128 result = a + b + c + d + carries;
+        return result;*/
+    }
+
+    inline std::pair<Fixed128, uint32_t> mul(uint32_t factor) const {
+        uint32_t quarters[4] = {
+            (upper >> 32) & 0xFFFFFFFF,
+            upper & 0xFFFFFFFF,
+            (lower >> 32) & 0xFFFFFFFF,
+            lower & 0xFFFFFFFF
+        };
+        uint32_t newQ[4];
+        uint32_t carry = 0;
+        for (int i = 3; i >= 0; i--) {
+            int64_t prod = int64_t(quarters[i]) * factor + carry;
+            newQ[i] = prod & 0xFFFFFFFF;
+            carry = prod >> 32;
+        }
+        /*    newQ[i] = quarters[i] * factor;
+        uint64_t tempLower = newQ[3];
+        uint64_t newLower = tempLower + (newQ[2] << 32);
+        uint64_t newUpper = (newQ[2] >> 32) + newQ[1] + (newQ[0] << 32) + (newLower < tempLower ? 1 : 0);*/
+        return std::make_pair(Fixed128{ newQ[0], newQ[1], newQ[2], newQ[3] }, carry);
+    }
+
+    /*
+    inline void arshift(int fac32) {
+        uint32_t temp = 0;
+        switch (fac32) {
+        case 0:
+            return;
+        case 1:
+            temp = upper & 0xFFFFFFFF;
+            upper = uint64_t(int64_t(upper) >> 32);
+            lower >>= 32;
+            lower |= uint64_t(temp) << 32;
+        case 2:
+            lower = upper;
+            upper = uint64_t(int64_t(upper) >> 63);
+        case 3:
+            lower = uint64_t(int64_t(upper) >> 32);
+            upper = uint64_t(int64_t(upper) >> 63);
+        default:
+            lower = uint64_t(int64_t(upper) >> 63);
+            upper = uint64_t(int64_t(upper) >> 63);
+        }
+    }*/
+    /*
+    inline Fixed128 operator * (const Fixed128& other) const {
+        int32_t quarters[4] = {
+            (upper >> 32) & 0xFFFFFFFF,
+            upper & 0xFFFFFFFF,
+            (lower >> 32) & 0xFFFFFFFF,
+            lower & 0xFFFFFFFF
+        };
+
+        int32_t otherQuarters[4] = {
+            (other.upper >> 32) & 0xFFFFFFFF,
+            other.upper & 0xFFFFFFFF,
+            (other.lower >> 32) & 0xFFFFFFFF,
+            other.lower & 0xFFFFFFFF
+        };
+
+        int64_t prods[4][4];
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 4 && j + i < 5; j++) {
+                if (i == 0 || j == 0)
+                    prods[i][j] = int64_t(quarters[i]) * int64_t(otherQuarters[j]);
+                else
+                    prods[i][j] = uint64_t(uint32_t(quarters[i])) * uint64_t(uint32_t(otherQuarters[j]));
+            }
+        }
+
+        Fixed128 ret = { 0, 0 };
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 4 && j + i < 5; j++) {
+                if (i == 0 || j == 0)
+                    ret.addSigned(prods[i][j], i + j);
+                else
+                    ret.add(prods[i][j], i + j);
+            }
+        }
+        return ret;
+        
+        /*
+        int64_t x00 = int64_t(quarters[0]) * int64_t(otherQuarters[0]);
+        int64_t x01 = int64_t(quarters[0]) * int64_t(otherQuarters[1]);
+        int64_t x02 = int64_t(quarters[0]) * int64_t(otherQuarters[2]);
+        int64_t x03 = int64_t(quarters[0]) * int64_t(otherQuarters[3]);
+        int64_t x10 = int64_t(quarters[1]) * int64_t(otherQuarters[0]);
+        int64_t x11 = int64_t(quarters[1]) * int64_t(otherQuarters[1]);
+        int64_t x12 = int64_t(quarters[1]) * int64_t(otherQuarters[2]);
+        int64_t x13 = int64_t(quarters[1]) * int64_t(otherQuarters[3]);
+        int64_t x20 = int64_t(quarters[2]) * int64_t(otherQuarters[0]);
+        int64_t x21 = int64_t(quarters[2]) * int64_t(otherQuarters[1]);
+        int64_t x22 = int64_t(quarters[2]) * int64_t(otherQuarters[2]);
+        int64_t x30 = int64_t(quarters[3]) * int64_t(otherQuarters[0]);
+        int64_t x31 = int64_t(quarters[3]) * int64_t(otherQuarters[1]);
+
+        Fixed128 ret = { 0, 0 };
+        /*uint32_t newQuarters[4] = {
+            x00,
+            x01 + x10,
+            x02 + x11 + x20,
+            x03 + x12 + x21 + x30,
+        };*//*
+        ret.add(x00, 0);
+        ret.add(x01 + x10, 1);
+        ret.add(x02 + x11 + x20, 2);
+        ret.add(x03 + x12 + x21 + x30, 3);
+        ret.add(x13 + x22 + x31, 4);
+
+        return ret;*/
+    /*}*/
+
+private:
+    inline void add(uint64_t val, int b32offset) {
+        switch (b32offset) {
+        case 0:
+            upper += val << 32;
+            return;
+        case 1:
+            upper += val;
+            return;
+        case 2:
+            upper += val >> 32;
+            lower += val << 32;
+            return;
+        case 3: {
+            uint64_t newLower = lower + val;
+            if (newLower < lower) upper++;
+            lower = newLower;
+            return;
+        }
+        case 4:
+            uint64_t newLower = lower + (val >> 32);
+            if (lower > newLower) upper++;
+            lower += newLower;
+            return;
+        }
+    }
+    inline void addSigned(int64_t val, int b32offset) {
+        switch (b32offset) {
+        case 0:
+            upper += val << 32;
+            return;
+        case 1:
+            upper += val;
+            return;
+        case 2:
+            upper += val >> 32;
+            lower += val << 32;
+            return;
+        case 3:
+            lower += val;
+            if (val < 0) upper--;
+            return;
+        
+        case 4: {
+            uint64_t newLower = lower + (val >> 32);
+            if (lower > newLower) upper++;
+            lower = newLower;
+            return;
+        }
+        default:
+            if (val < 0) {
+                if (lower == 0) upper--;
+                lower--;
+            }
+            return;
+        }
+    }
+public:
+
+    bool isNegative(void) const {
+        return (upper & (uint64_t(1) << 63)) != 0;
+    }
+
+    operator double(void) const {
+        const int64_t twoToThe32 = 0x100000000ULL;
+        return double(int64_t(upper)) / twoToThe32 + int64_t(lower) / twoToThe32 / twoToThe32 / twoToThe32;
+    }
+
+    inline Fixed128 operator ~ (void) const {
+        return Fixed128{ ~upper, ~lower };
+    }
+
+    inline bool operator == (const Fixed128& other) const {
+        return upper == other.upper && lower == other.lower;
+    }
+
+    inline bool operator != (const Fixed128& other) const {
+        return !operator==(other);
+    }
+
+    inline bool operator < (const Fixed128& other) const {
+        return upper < other.upper || (upper == other.upper && lower < other.lower);
+    }
+
+    inline bool operator <= (const Fixed128& other) const {
+        return operator<(other) || operator==(other);
+    }
+
+    inline bool operator > (const Fixed128& other) const {
+        return upper > other.upper || (upper == other.upper && lower > other.lower);
+    }
+
+    inline bool operator >= (const Fixed128& other) const {
+        return operator>(other) || operator==(other);
+    }
+};
+
+struct Fixed64
+{
+    bool sign;
+    uint64_t bits;
+
+    Fixed64(const Fixed64&) = default;
+    ~Fixed64() = default;
+
+
+    inline Fixed64(uint64_t bits, bool dummy) :
+        bits{ bits }
+    {
+    }
+
+    inline Fixed64(double x)
+    {
+        if (x < 0) {
+            sign = true;
+            x *= -1;
+        }
+        else {
+            sign = false;
+        }
+        int integerPart = int(x);
+        double fractionalPart = x - integerPart;
+        bits = uint64_t(integerPart) << 32;
+        bits |= uint64_t(fractionalPart * (1ULL << 32)) & 0xFFFFFFFF;
+    }
+
+    inline Fixed64 operator + (const Fixed64& other) {
+        return Fixed64{ bits + other.bits, true };
+    }
+    
+    inline Fixed64& operator +=(const Fixed64& other) {
+        bits += other.bits;
+        return *this;
+    }
+
+    inline Fixed64 operator - (const Fixed64& other) {
+        return Fixed64{ bits - other.bits, true };
+    }
+
+    inline Fixed64 operator * (const Fixed64& other) {
+        /*int32_t upper = bits >> 32;
+        uint32_t lower = uint32_t(bits & 0xFFFFFFFF);
+        int64_t upup = int64_t(upper) * int64_t(upper);
+        int64_t loup = int64_t(upper) * int64_t(lower);
+        int64_t lolo = int64_t(lower) * int64_t(lower);
+
+        int32_t newUp = upup & 0xFFFFFFFF + (loup >> 32);
+        int32_t newLo = loup & 0xFFFFFFFF + (lolo >> 32);*/
+        double d = int32_t(bits >> 32) + double(uint32_t(bits)) / (1ULL << 32);
+        double od = int32_t(other.bits >> 32) + double(uint32_t(other.bits)) / (1ULL << 32);
+        return d * od * (other.sign != sign) ? -1 : 1;
+
+        //return Fixed64{ (uint64_t(newUp) << 32) | newLo, true };
+    }
+
+    inline bool operator == (const Fixed64& other) {
+        return bits == other.bits;
+    }
+
+    inline bool operator != (const Fixed64& other) {
+        return !operator==(other);
+    }
+
+    inline bool operator < (const Fixed64& other) {
+        return bits < other.bits;
+    }
+
+    inline bool operator <= (const Fixed64& other) {
+        return operator<(other) || operator==(other);
+    }
+
+    inline bool operator > (const Fixed64& other) {
+        return bits > other.bits;
+    }
+
+    inline bool operator >= (const Fixed64& other) {
+        return operator>(other) || operator==(other);
+    }
+};
+
+struct Fixed32
+{
+    int32_t bits;
+
+    Fixed32(const Fixed32&) = default;
+    ~Fixed32() = default;
+
+
+    inline Fixed32(int32_t bits, bool dummy) :
+        bits{ bits }
+    {
+    }
+
+    inline Fixed32(double x)
+    {
+        int integerPart = ::floor(x);
+        double fractionalPart = x - integerPart;
+        /*if (x < 0) {
+            integerPart--;
+            fractionalPart = 1.0 - fractionalPart;
+        }*/
+        bits = int32_t(integerPart) << 16;
+        bits |= uint32_t(fractionalPart * (1ULL << 16)) & 0xFFFF;
+    }
+
+    inline Fixed32 operator + (const Fixed32& other) {
+        return Fixed32{ bits + other.bits, true };
+    }
+    
+    inline Fixed32& operator +=(const Fixed32& other) {
+        bits += other.bits;
+        return *this;
+    }
+
+    inline Fixed32 operator - (const Fixed32& other) {
+        return Fixed32{ bits - other.bits, true };
+    }
+
+    inline Fixed32 operator * (const Fixed32& other) {
+        int64_t prod = int64_t(bits) * int64_t(other.bits);
+        return Fixed32{ int32_t(prod >> 16), true };
+        //return Fixed32{ (uint64_t(newUp) << 32) | newLo, true };
+    }
+
+    inline bool operator == (const Fixed32& other) {
+        return bits == other.bits;
+    }
+
+    inline bool operator != (const Fixed32& other) {
+        return !operator==(other);
+    }
+
+    inline bool operator < (const Fixed32& other) {
+        return bits < other.bits;
+    }
+
+    inline bool operator <= (const Fixed32& other) {
+        return operator<(other) || operator==(other);
+    }
+
+    inline bool operator > (const Fixed32& other) {
+        return bits > other.bits;
+    }
+
+    inline bool operator >= (const Fixed32& other) {
+        return operator>(other) || operator==(other);
+    }
+};
+

+ 217 - 0
Generators.cpp

@@ -0,0 +1,217 @@
+#include "Generators.h"
+
+#include "GenericMandelbrot.h"
+#include "Fixed.h"
+
+#include <iostream>
+#include <iterator>
+
+
+using namespace cl;
+
+Platform getPlatform() {
+    /* Returns the first platform found. */
+    std::vector<Platform> all_platforms;
+    Platform::get(&all_platforms);
+
+    if (all_platforms.size()==0) {
+        std::cout << "No platforms found. Check OpenCL installation!\n";
+        exit(1);
+    }
+    return all_platforms[0];
+}
+
+
+Device getDevice(Platform platform, int i, bool display = false) {
+    /* Returns the deviced specified by the index i on platform.
+    * If display is true, then all of the platforms are listed.
+    */
+    std::vector<Device> all_devices;
+    platform.getDevices(CL_DEVICE_TYPE_ALL, &all_devices);
+    if (all_devices.size() == 0) {
+        std::cout << "No devices found. Check OpenCL installation!\n";
+        exit(1);
+    }
+
+    if (display) {
+        for (::size_t j = 0; j < all_devices.size(); j++)
+            printf("Device %d: %s\n", int(j), all_devices[j].getInfo<CL_DEVICE_NAME>().c_str());
+    }
+    return all_devices[i];
+}
+
+
+ClGenerator::ClGenerator(void)
+{
+    Platform p = getPlatform();
+    device = getDevice(p, 0, false);
+    context = Context{ device };
+    Program::Sources sources;
+
+    std::string kcode_alt =
+        "void kernel iterate(global float* A, const int width, float xl, float yt, float pixelScale, int max) {"
+        "   int x = get_global_id(0) % width;"
+        "   int y = get_global_id(0) / width;"
+        "   float a = x * pixelScale + xl;"
+        "   float b = y * pixelScale + yt;"
+        "   float ca = a;"
+        "   float cb = b;"
+        ""
+        "   int n = 0;"
+        "   while (n < max) {"
+        "       float aa = a * a;"
+        "       float bb = b * b;"
+        "       float ab = a * b;"
+        "       a = aa - bb + ca;"
+        "       b = 2 * ab + cb;"
+        "       if (aa + bb > 16) break;"
+        "       n++;"
+        "   }\n"
+        "   A[get_global_id(0)] = n;//((float)n) + (a + b - 16) / (256 - 16);\n"
+//        "   A[get_global_id(0)] = 5;"
+        "}";
+
+    std::string kcode =
+        "void kernel iterate(global float* A, const int width, float xl, float yt, float pixelScale, int max) {"
+        "   int x = get_global_id(0) % (width);"
+        "   int y = get_global_id(0) / (width);"
+        "   float a = x * pixelScale + xl;"
+        "   float b = y * pixelScale + yt;"
+        "   float ca = a;"
+        "   float cb = b;"
+        ""
+        "   int n = 0;"
+        "   while (n < max) {"
+        "       float aa = a * a;"
+        "       float bb = b * b;"
+        "       float ab = a * b;"
+        "       a = aa - bb + ca;"
+        "       b = 2 * ab + cb;"
+        "       if (aa + bb > 16) break;"
+        "       n++;"
+        "   }\n"
+        "   A[get_global_id(0)] = n;//((float)n) + (a + b - 16) / (256 - 16);\n"
+//        "   A[get_global_id(0)] = 5;"
+        "}";
+
+    sources.push_back({ kcode.c_str(), kcode.length() });
+
+    program = Program{ context, sources };
+    if (program.build({ device }) != CL_SUCCESS) {
+        std::cout << "Error building: " << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device) << std::endl;
+        exit(1);
+    }
+
+    queue = CommandQueue(context, device);
+}
+
+/*Bitmap<RGBColor> ClGenerator::generate(const MandelInfo& info)
+{
+    return enqueueMandelbrot(info.bWidth, info.bHeight, info.view.x, info.view.y, info.view.width).get();
+}*/
+
+Bitmap<float> ClGenerator::generateRaw(const MandelInfo& info)
+{
+    ::size_t bufferSize = info.bWidth * info.bHeight * sizeof(float);
+    Bitmap<float> bitmap{ info.bWidth, info.bHeight };
+    Buffer buffer_A(context, CL_MEM_WRITE_ONLY, bufferSize);
+    float pixelScale = info.view.width / info.bWidth;
+
+    Kernel iterate = Kernel(program, "iterate");
+    iterate.setArg(0, buffer_A);
+    iterate.setArg(1, info.bWidth);
+    iterate.setArg(2, float(info.view.x));
+    iterate.setArg(3, float(info.view.y));
+    iterate.setArg(4, pixelScale);
+    iterate.setArg(5, info.maxIter);
+
+    queue.enqueueNDRangeKernel(iterate, NullRange, NDRange(info.bWidth * info.bHeight), NDRange(32));
+    queue.enqueueReadBuffer(buffer_A, CL_TRUE, 0, bufferSize, bitmap.pixels.get());
+
+    return bitmap;
+}
+
+std::future<Bitmap<RGBColor>> ClGenerator::enqueueMandelbrot(long width, long height, float x, float y, float fwidth)
+{
+    x = x - fwidth / 2;
+    y = y - fwidth * height / width / 2;
+    auto mandelCreator = [width, height, x, y, fwidth, this] () -> Bitmap<RGBColor> {
+        ::size_t bufferSize = width * height * sizeof(float);
+        Bitmap<float> bitmap{ width, height };
+        Buffer buffer_A(context, CL_MEM_WRITE_ONLY, bufferSize);
+        //CommandQueue queue(context, device);
+        //queue.enqueueWriteBuffer(buffer_A, CL_TRUE, 0, bufferSize, A);
+
+        /*float x = -2.3;
+        float y = -1.5;*/
+        float pixelScale = fwidth / width;
+
+        Kernel iterate = Kernel(program, "iterate");
+        iterate.setArg(0, buffer_A);
+        iterate.setArg(1, width);
+        iterate.setArg(2, x);
+        iterate.setArg(3, y);
+        iterate.setArg(4, pixelScale);
+
+        queue.enqueueNDRangeKernel(iterate, NullRange, NDRange(width * height), NDRange(32));
+
+
+        queue.enqueueReadBuffer(buffer_A, CL_TRUE, 0, bufferSize, bitmap.pixels.get());
+
+        auto converted = bitmap.map<RGBColor>([](float i) { return i < 0 ? RGBColor{ 0,0,0 } : RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127), uint8_t(sin(i * 0.01f) * 127 + 127), uint8_t(i) }; });//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
+        return converted;
+    };
+    //return std::future<Bitmap<RGBColor>(mandelCreator(), );
+    return std::async(/*std::launch::deferred,*/ mandelCreator);
+}
+
+
+/*
+std::future<Bitmap<RGBColor>> createMandelbrot()
+{
+    auto mandelCreator = [] () -> Bitmap<RGBColor> {
+        Bitmap<int> bitmap{1024, 1024};
+        calculateMandel(bitmap);
+        return bitmap.map<RGBColor>([](int x) { return RGBColor{ unsigned char(x), unsigned char(x), unsigned char(x) }; });
+    };
+    return std::async(mandelCreator);
+}
+
+*/
+
+std::future<Bitmap<RGBColor>> createHPM()
+{
+    /*auto mandelCreator = [] () -> Bitmap<RGBColor> {
+        Fixed128 smallFact { 10000ULL, 0 };
+        Bitmap<float> bitmap{ 128, 128 };
+        for (::size_t y = 0; y < bitmap.height; y++) {
+            for (::size_t x = 0; x < bitmap.width; x++) {
+                Fixed128 a = Fixed128(x) * smallFact;
+                Fixed128 b = Fixed128(y) * smallFact;
+                bitmap.get(x, y) = iterate<Fixed128>(a, b, 250);
+            }
+        }
+        return bitmap.map<RGBColor>([](float i) { return i < 0 ? RGBColor{ 0,0,0 } : RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127), uint8_t(sin(i * 0.01f) * 127 + 127), uint8_t(i) }; });//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
+    };*/
+    double xx = -10.6;
+    double yy = 4.7;
+    Fixed128 x = xx;
+    Fixed128 y = yy;
+
+    std::cout << double(-x) << " * " << double(-y) << " = " << double(x * y) << " --> " << (xx * yy) << std::endl;
+    //exit(0);
+
+    auto mandelCreator = [] () -> Bitmap<RGBColor> {
+        Bitmap<float> bitmap{ 512, 512 };
+        for (::size_t y = 0; y < bitmap.height; y++) {
+            for (::size_t x = 0; x < bitmap.width; x++) {
+                Fixed128 a = x * 2.0 / bitmap.width - 1;
+                Fixed128 b = y * 2.0 / bitmap.height - 1;
+                bitmap.get(x, y) = iterate<Fixed128>(a, b, 250);
+            }
+        }
+        return bitmap.map<RGBColor>([](float i) { return i < 0 ? RGBColor{ 0,0,0 } : RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127), uint8_t(sin(i * 0.01f) * 127 + 127), uint8_t(i) }; });//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
+    };
+
+    return std::async(mandelCreator);
+}

+ 184 - 0
Generators.h

@@ -0,0 +1,184 @@
+#pragma once
+#ifndef MANDELQUEUE_H_
+#define MANDELQUEUE_H_
+
+#include "QueueManager.h"
+#include "GenericMandelbrot.h"
+#include <omp.h>
+#include <future>
+#include <cstdlib>
+
+#ifdef __APPLE__
+#include <OpenCL/cl.hpp>
+#else
+#include <CL/cl.hpp>
+#endif
+
+#include <immintrin.h>
+
+
+class ClGenerator : public MandelGenerator
+{
+    cl::Device device;
+    cl::Context context;
+    cl::Program program;
+    cl::CommandQueue queue;
+public:
+    ClGenerator(void);
+    ~ClGenerator(void) = default;
+
+    //Bitmap<RGBColor> generate(const MandelInfo& info);
+    Bitmap<float> generateRaw(const MandelInfo& info);
+
+    std::future<Bitmap<RGBColor>> enqueueMandelbrot(long width, long height, float x, float y, float fwidth);
+};
+
+
+std::future<Bitmap<RGBColor>> createHPM();
+
+
+template<typename T>
+class CpuGenerator : public MandelGenerator
+{
+public:
+
+    Bitmap<float> generateRaw(const MandelInfo& info)
+    {
+        const MandelViewport& view = info.view;
+        Bitmap<float> res{ info.bWidth, info.bHeight };
+
+        omp_set_num_threads(2 * omp_get_num_procs());
+#pragma omp parallel for
+        for (int j = 0; j < res.height; j++) {
+            T y = T(view.y) + T(j) * view.height / res.height;
+            for (::size_t i = 0; i < res.width; i++) {
+                T x = T(view.x) + T(i) * view.width / res.width;
+                res.get(i, j) = iterate<T>(x, y, info.maxIter);
+            }
+        }
+        return res;
+    }
+};
+
+
+template<>
+Bitmap<float> CpuGenerator<double>::generateRaw(const MandelInfo& info)
+{
+    using T = double;
+    const MandelViewport& view = info.view;
+    Bitmap<float> res{ info.bWidth, info.bHeight };
+
+    omp_set_num_threads(2 * omp_get_num_procs());
+#pragma omp parallel for
+    for (long j = 0; j < res.height; j++) {
+        T y = T(view.y) + T(j) * view.height / res.height;
+        long i = 0;
+        for (i; i < res.width; i += 4) {
+            __m256d xs = {
+                double(view.x) + double(i) * view.width / res.width,
+                double(view.x) + double(i + 1) * view.width / res.width,
+                double(view.x) + double(i + 2) * view.width / res.width,
+                double(view.x) + double(i + 3) * view.width / res.width
+            };
+
+            int itRes[4] = {0, 0, 0, 0};
+
+            __m256d threshold = {16.0, 16.0, 16.0, 16.0};
+            __m256d counter = {0, 0, 0, 0};
+            __m256d adder = {1, 1, 1, 1};
+
+            __m256d ys = {y, y, y, y};
+            __m256d a = xs;
+            __m256d b = ys;
+
+            for (int k = 0; k < info.maxIter; k++) {
+                __m256d aa = _mm256_mul_pd(a, a);
+                __m256d bb = _mm256_mul_pd(b, b);
+                __m256d abab = _mm256_mul_pd(a, b); abab = _mm256_add_pd(abab, abab);
+                a = _mm256_add_pd(_mm256_sub_pd(aa, bb), xs);
+                b = _mm256_add_pd(abab, ys);
+                __m256i cmp = _mm256_castpd_si256(_mm256_cmp_pd(_mm256_add_pd(aa, bb), threshold, _CMP_LE_OQ));
+                adder = _mm256_and_pd(adder, _mm256_castsi256_pd(cmp));
+                counter = _mm256_add_pd(counter, adder);
+                if (_mm256_testz_si256(cmp, cmp) != 0) {
+                    break;
+                }
+            }
+            double data[8];
+            void* aligned = data;
+            unsigned int length = sizeof data;
+            std::align(32, 4 * sizeof(double), aligned, length);
+            double* ftRes = static_cast<double*>(aligned);
+            _mm256_store_pd(ftRes, counter);
+            for (int k = 0; k < 4; k++)
+                res.get(i + k, j) = ftRes[k] > 0 ? float(ftRes[k]) : info.maxIter;
+        }
+    }
+
+    return res;
+}
+
+
+template<>
+Bitmap<float> CpuGenerator<float>::generateRaw(const MandelInfo& info)
+{
+    using T = float;
+    const MandelViewport& view = info.view;
+    Bitmap<float> res{ info.bWidth, info.bHeight };
+
+    omp_set_num_threads(2 * omp_get_num_procs());
+#pragma omp parallel for
+    for (long j = 0; j < res.height; j++) {
+        T y = T(view.y) + T(j) * view.height / res.height;
+        long i = 0;
+        for (i; i < res.width; i += 8) {
+            __m256 xs = {
+                float(view.x + double(i) * view.width / res.width),
+                float(view.x + double(i + 1) * view.width / res.width),
+                float(view.x + double(i + 2) * view.width / res.width),
+                float(view.x + double(i + 3) * view.width / res.width),
+                float(view.x + double(i + 4) * view.width / res.width),
+                float(view.x + double(i + 5) * view.width / res.width),
+                float(view.x + double(i + 6) * view.width / res.width),
+                float(view.x + double(i + 7) * view.width / res.width)
+            };
+
+            __m256 counter = {0, 0, 0, 0, 0, 0, 0, 0};
+            __m256 adder = {1, 1, 1, 1, 1, 1, 1, 1};
+
+            __m256 threshold = {16.0f, 16.0f, 16.0f, 16.0f, 16.0f, 16.0f, 16.0f, 16.0f};
+
+            __m256 ys = {y, y, y, y, y, y, y, y};
+            __m256 a = xs;
+            __m256 b = ys;
+
+            for (int k = 0; k < info.maxIter; k++) {
+                __m256 aa = _mm256_mul_ps(a, a);
+                __m256 bb = _mm256_mul_ps(b, b);
+                __m256 abab = _mm256_mul_ps(a, b); abab = _mm256_add_ps(abab, abab);
+                a = _mm256_add_ps(_mm256_sub_ps(aa, bb), xs);
+                b = _mm256_add_ps(abab, ys);
+                __m256i cmp = _mm256_castps_si256(_mm256_cmp_ps(_mm256_add_ps(aa, bb), threshold, _CMP_LE_OQ));
+                adder = _mm256_and_ps(adder, _mm256_castsi256_ps(cmp));
+                counter = _mm256_add_ps(counter, adder);
+                if (_mm256_testz_si256(cmp, cmp) != 0) {
+                    break;
+                }
+            }
+            float data[16];
+            void* aligned = data;
+            unsigned int length = sizeof data;
+            std::align(32, 8 * sizeof(float), aligned, length);
+            float* ftRes = static_cast<float*>(aligned);
+            _mm256_store_ps(ftRes, counter);
+            for (int k = 0; k < 8; k++)
+                res.get(i + k, j) = ftRes[k] > 0 ? ftRes[k] : info.maxIter;
+        }
+    }
+
+    return res;
+}
+
+
+
+#endif // MANDELQUEUE_H_

+ 24 - 0
GenericMandelbrot.h

@@ -0,0 +1,24 @@
+#pragma once
+
+
+template<typename T>
+float iterate(const T& ca, const T& cb, int maxIterations)
+{
+    T a = ca;
+    T b = cb;
+
+    int n;
+    for(n = 0; n < maxIterations; n++) {
+        T aa = a * a;
+        T bb = b * b;
+        T abab = a * b; abab += abab;
+
+        a = aa - bb + ca;
+        b = abab + cb;
+        if (aa + bb > T(16)) {
+            break;
+        }
+    }
+
+    return float(n);
+}

+ 207 - 0
MandelWidget.cpp

@@ -0,0 +1,207 @@
+#include "MandelWidget.h"
+
+#include <QOpenGLVertexArrayObject>
+
+Texture::Texture(const Bitmap<RGBColor>& bitmap)
+{
+    glGenTextures(1, &id);
+    glBindTexture(GL_TEXTURE_2D, id);
+
+    long lineLength = (bitmap.width * 3 + 3) & ~3;
+
+    unsigned char* pixels = new unsigned char[lineLength * bitmap.height];
+    for (int i = 0; i < bitmap.width; i++) {
+        for (int j = 0; j < bitmap.height; j++) {
+            int index = i * 3 + j * lineLength;
+            RGBColor c = bitmap.get(i, j);
+            pixels[index] = c.r;
+            pixels[index + 1] = c.g;
+            pixels[index + 2] = c.b;
+        }
+    }
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(bitmap.width), int(bitmap.height), 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+
+Texture::~Texture(void)
+{
+    glDeleteTextures(1, &id);
+}
+
+
+void Texture::bind(void) const
+{
+    glBindTexture(GL_TEXTURE_2D, id);
+}
+
+
+void Texture::drawRect(float x, float y, float width, float height)
+{
+    glColor3ub(255, 255, 255);
+    glEnable(GL_TEXTURE_2D);
+    bind();
+    glBegin(GL_TRIANGLE_STRIP);
+    glTexCoord2f(0, 0);
+    glVertex2f(x, y);
+    glTexCoord2f(1, 0);
+    glVertex2f(x + width, y);
+    glTexCoord2f(0, 1);
+    glVertex2f(x, y + height);
+    glTexCoord2f(1, 1);
+    glVertex2f(x + width, y + height);
+    glEnd();
+    glDisable(GL_TEXTURE_2D);
+}
+
+
+MandelWidget::MandelWidget(QWidget* parent) :
+    QGLWidget{ QGLFormat(QGL::SampleBuffers), parent }
+{
+    this->setContentsMargins(0, 0, 0, 0);
+    this->setSizePolicy(QSizePolicy::Expanding,
+        QSizePolicy::Expanding);
+}
+
+
+MandelWidget::~MandelWidget()
+{
+}
+
+
+void MandelWidget::initializeGL(void)
+{
+    qglClearColor(Qt::black);
+
+    glDisable(GL_DEPTH_TEST);
+    //glShadeModel(GL_SMOOTH);
+
+    /*CpuGenerator<double> cpg;
+    MandelInfo mi;
+    mi.bWidth = this->width();//ql.geometry().width();
+    mi.bHeight = this->height(); //ql.geometry().height();
+    mi.maxIter = 250;
+    mi.view = viewport;
+    auto bitmap = cpg.generate(mi);*/
+    Bitmap<RGBColor> bitmap(1, 1);
+    bitmap.get(0, 0) = RGBColor{5, 150, 50};
+
+    tex = std::make_unique<Texture>(bitmap);
+}
+
+
+void MandelWidget::paintGL(void)
+{
+    int width = this->width();
+    int height = this->height();
+
+    CpuGenerator<double> cpg;
+    ClGenerator clg;
+    MandelGenerator& mg = cpg;
+    MandelInfo mi;
+    mi.bWidth = width; //ql.geometry().width();
+    mi.bHeight = height; //ql.geometry().height();
+    mi.maxIter = 5000;
+    mi.view = viewport;
+    auto bitmap = mg.generate(mi);
+    /*Bitmap<RGBColor> bitmap(1000, 1000);
+    for (int i = 0; i < 1000 * 1000; i++)
+        bitmap.pixels[i] = RGBColor{5, uint8_t((i % 1000) ^ (i / 1000)), 50};*/
+    tex = std::make_unique<Texture>(bitmap);
+
+    glViewport(0, 0, width, height);
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+#ifdef QT_OPENGL_ES_1
+    glOrthof(0, width, height, 0, -1.0, 1.0);
+#else
+    glOrtho(0, width, height, 0, -1.0, 1.0);
+#endif
+    glMatrixMode(GL_MODELVIEW);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    glLoadIdentity();
+    tex->drawRect(0, 0, width, height);
+
+    drawRubberband();
+    printf("omg\n");
+}
+
+
+void MandelWidget::drawRubberband(void)
+{
+    glColor3ub(10, 200, 10);
+    glBegin(GL_LINE_LOOP);
+    glVertex2d(rubberband.x(), rubberband.y());
+    glVertex2d(rubberband.right(), rubberband.y());
+    glVertex2d(rubberband.right(), rubberband.bottom());
+    glVertex2d(rubberband.x(), rubberband.bottom());
+    glEnd();
+}
+
+
+void MandelWidget::resizeGL(int width, int height)
+{
+}
+
+
+void MandelWidget::redraw(void)
+{
+    /*CpuGenerator<double> cpg;
+    MandelInfo mi;
+    mi.bWidth = this->geometry().width();//ql.geometry().width();
+    mi.bHeight = this->geometry().height(); //ql.geometry().height();
+    mi.maxIter = 250;
+    mi.view = viewport;*/
+    update();
+
+    //auto bitmap = cpg.generate(mi).map<uint32_t>([](RGBColor rgb) { return 255 << 24 | rgb.b << 16 | rgb.g << 8 | rgb.r; });
+}
+
+
+void MandelWidget::resizeEvent(QResizeEvent* re)
+{
+    double aspect = double(geometry().width()) / geometry().height();
+
+    //if (viewport.width > viewport.height * aspect)
+        viewport.height = (viewport.width / aspect);
+    //else
+    //    viewport.width = (viewport.height * aspect);
+    
+    redraw();
+}
+
+
+void MandelWidget::mousePressEvent(QMouseEvent* me)
+{
+    rubberband.setCoords(me->x(), me->y(), 0, 0);
+}
+
+
+void MandelWidget::mouseMoveEvent(QMouseEvent* me)
+{
+    QRectF& rect = rubberband;
+    float aspect = float(geometry().width()) / geometry().height();
+    rect.setBottomRight(QPoint(me->x(), me->y()));
+    if (rect.width() > rect.height() * aspect)
+        rect.setHeight(rect.width() / aspect);
+    else
+        rect.setWidth(rect.height() * aspect);
+}
+
+
+void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
+{
+    QRect rect = rubberband.toRect();
+    QRect full = this->geometry();
+
+    viewport.x += double(rect.left()) * viewport.width / full.width();
+    viewport.y += double(rect.top()) * viewport.height / full.height();
+    viewport.width *= double(rect.width()) / full.width();
+    viewport.height *= double(rect.height()) / full.height();
+    redraw();
+}

+ 57 - 0
MandelWidget.h

@@ -0,0 +1,57 @@
+#pragma once
+
+#include <QGLWidget>
+#include <qopengl.h>
+#include <qopenglfunctions.h>
+#include <qopenglcontext.h>
+#include <qscrollarea.h>
+#include <qlabel.h>
+#include <qevent.h>
+#include <qrubberband.h>
+#include "Generators.h"
+
+class Texture
+{
+    GLuint id;
+public:
+    Texture(const Bitmap<RGBColor>& pict);
+    ~Texture(void);
+
+    void bind(void) const;
+
+    void drawRect(float x, float y, float width, float height);
+};
+
+class MandelWidget : public QGLWidget
+{
+    Q_OBJECT
+private:
+    //QScrollArea qsa;
+    //QLabel ql;
+    QRectF rubberband;
+
+    std::unique_ptr<Texture> tex;
+    MandelViewport viewport;
+public:
+    MandelWidget(QWidget* parent = nullptr);
+    ~MandelWidget(void) override;
+
+    void initializeGL(void) override;
+
+    void resizeGL(int w, int h) override;
+
+    void paintGL() override;
+
+    void drawRubberband(void);
+
+    void redraw();
+
+    void resizeEvent(QResizeEvent* re) override;
+    void mousePressEvent(QMouseEvent* me) override;
+    void mouseMoveEvent(QMouseEvent* me) override;
+    void mouseReleaseEvent(QMouseEvent* me) override;
+
+    inline const MandelViewport& getViewport(void) const { return viewport; }
+signals:
+};
+

+ 30 - 0
QueueManager.cpp

@@ -0,0 +1,30 @@
+#include "QueueManager.h"
+
+void MandelViewport::adjustAspectRatio(double nwidth, double nheight)
+{
+    double otherRatio = nwidth / nheight;
+    if (width < height * otherRatio)
+        width = height * otherRatio;
+    else if (height < width / otherRatio)
+            height = width / otherRatio;
+}
+
+MandelGenerator::~MandelGenerator(void)
+{
+}
+
+Bitmap<RGBColor> MandelGenerator::generate(const MandelInfo& mandelInfo)
+{
+    auto converter = [](float i) { return i < 0 ? RGBColor{ 0,0,0 } : RGBColor{ uint8_t(cos(i * 0.15f) * 127 + 127), uint8_t(sin(i * 0.01f) * 127 + 127), uint8_t(sin(i * 0.04f) * 127 + 127) }; };
+    return generateRaw(mandelInfo).map<RGBColor>(converter);
+}
+
+
+QueueManager::QueueManager()
+{
+}
+
+
+QueueManager::~QueueManager()
+{
+}

+ 68 - 0
QueueManager.h

@@ -0,0 +1,68 @@
+#pragma once
+#ifndef QUEUEMANAGER_H_
+#define QUEUEMANAGER_H_
+
+#include <cinttypes>
+#include <vector>
+#include <future>
+#include "Bitmap.h"
+
+struct MandelViewport
+{
+    /// real part of the top left corner
+    double x = -2.1;
+
+    /// imaginary part of the top left corner
+    double y = -1.5;
+
+    /// real-part span of the picture to be generated
+    double width = 3;
+
+    /// imaginary-part span of the picture to be generated
+    double height = 3;
+
+    /*!
+     * \brief adjusts the aspect ratio of the viewport, making sure
+     *        the updated viewport contains all of the original one.
+     */
+    void adjustAspectRatio(double nwidth, double nheight);
+};
+
+struct MandelInfo
+{
+    /// viewport
+    MandelViewport view;
+
+    /// width of the bitmap to be generated
+    long bWidth;
+
+    /// height of the bitmap to be generated
+    long bHeight;
+    
+    /// maximum iterations
+    int maxIter;
+};
+
+
+class MandelGenerator
+{
+public:
+    MandelGenerator(void) = default;
+    virtual ~MandelGenerator(void);
+
+    virtual Bitmap<RGBColor> generate(const MandelInfo& mandelInfo);
+    virtual Bitmap<float> generateRaw(const MandelInfo& info) = 0;
+};
+
+
+class QueueManager
+{
+public:
+    QueueManager(void);
+    ~QueueManager(void);
+
+    std::future<Bitmap<RGBColor>> generate(const MandelInfo& mandelInfo);
+};
+
+#endif // QUEUEMANAGER_H_
+

+ 19 - 0
SectionManager.cpp

@@ -0,0 +1,19 @@
+#include "SectionManager.h"
+
+SectionManager::SectionManager(void)
+{
+
+}
+
+
+int SectionManager::getNextLevel(double chunkWidth)
+{
+    int level = 0;
+    double w = 1;
+    while(w >= chunkWidth) {
+        w = w / 2;
+        level++;
+    }
+    return level;
+}
+

+ 15 - 0
SectionManager.h

@@ -0,0 +1,15 @@
+#pragma once
+#ifndef SECTIONMANAGER_H
+#define SECTIONMANAGER_H
+
+#include "QueueManager.h"
+
+class SectionManager
+{
+public:
+    SectionManager(void);
+
+    int getNextLevel(double chunkWidth);
+};
+
+#endif // SECTIONMANAGER_H

+ 146 - 0
VideoStream.cpp

@@ -0,0 +1,146 @@
+#include "VideoStream.h"
+
+#include <iostream>
+
+
+
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
+#define av_frame_alloc  avcodec_alloc_frame
+#define av_frame_free  avcodec_free_frame
+#endif
+
+const uint8_t VideoStream::endcode[] = { 0, 0, 1, 0xb7 };
+
+
+VideoStream::VideoStream(::size_t width, ::size_t height, const std::string& filename) :
+    width{ width }, height{ height }
+{
+    avcodec_register_all();
+
+    codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
+    if (!codec) {
+        fprintf(stderr, "invalid codec\n");
+        exit(1);
+    }
+
+    codecContext = avcodec_alloc_context3(codec);
+    picture = av_frame_alloc();
+
+    pkt = av_packet_alloc();
+    if (!pkt)
+        exit(1);
+
+    codecContext->bit_rate = 500000 * 100;
+    codecContext->width = width;
+    codecContext->height = height;
+    codecContext->time_base = AVRational{ 1, 60 };
+    codecContext->framerate = AVRational{ 60, 1 };
+
+    codecContext->gop_size = 10; /* emit one intra frame every ten frames */
+    codecContext->max_b_frames = 1;
+    codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
+
+    if (avcodec_open2(codecContext, codec, nullptr) < 0) {
+        fprintf(stderr, "could not open codec\n");
+        exit(1);
+    }
+
+    file = fopen(filename.c_str(), "wb");
+    if (!file) {
+        fprintf(stderr, "could not open %s\n", filename.c_str());
+        exit(1);
+    }
+
+    picture->format = codecContext->pix_fmt;
+    picture->width  = codecContext->width;
+    picture->height = codecContext->height;
+
+    int retval = av_frame_get_buffer(picture, 0);
+    if (retval < 0) {
+        fprintf(stderr, "could not alloc the frame data\n");
+        exit(1);
+    }
+    //av_image_alloc(picture->data, picture->linesize, width, height, codecContext->pix_fmt, 32);
+
+    swsContext = sws_getContext(width, height,
+        AV_PIX_FMT_RGB24, width, height,
+        AV_PIX_FMT_YUV420P, 0, 0, 0, 0);
+}
+
+
+static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
+    FILE *outfile)
+{
+    int ret;
+
+    /* send the frame to the encoder */
+    ret = avcodec_send_frame(enc_ctx, frame);
+    if (ret < 0) {
+        fprintf(stderr, "error sending a frame for encoding\n");
+        exit(1);
+    }
+
+    while (ret >= 0) {
+        ret = avcodec_receive_packet(enc_ctx, pkt);
+        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+            return;
+        else if (ret < 0) {
+            fprintf(stderr, "error during encoding\n");
+            exit(1);
+        }
+
+        printf("encoded frame %3\"PRId64\" (size=%5d)\n", pkt->pts, pkt->size);
+        fwrite(pkt->data, 1, pkt->size, outfile);
+        av_packet_unref(pkt);
+    }
+}
+
+
+VideoStream::~VideoStream()
+{
+    /* flush the encoder */
+    encode(codecContext, NULL, pkt, file);
+
+    /* add sequence end code to have a real MPEG file */
+    fwrite(endcode, 1, sizeof(endcode), file);
+    fclose(file);
+
+    avcodec_free_context(&codecContext);
+    av_frame_free(&picture);
+    av_packet_free(&pkt);
+}
+
+
+void VideoStream::addFrame(const Bitmap<RGBColor>& frame)
+{
+    int retval = av_frame_make_writable(picture);
+    if (retval < 0)
+        exit(1);
+
+    /* prepare a dummy image */
+    /* Y */
+    /*for(int y = 0; y < height; y++) {
+        for(int x = 0; x < width; x++) {
+            picture->data[0][y * picture->linesize[0] + x] = frame.get(x, y).r / 2;
+        }
+    }*/
+
+    /* Cb and Cr */
+    /*for(int y=0;y<height / 2;y++) {
+        for(int x=0;x<width / 2;x++) {
+            picture->data[1][y * picture->linesize[1] + x] = frame.get(x * 2, y * 2).g / 2;
+            picture->data[2][y * picture->linesize[2] + x] = frame.get(x * 2, y * 2).b / 2;
+        }
+    }*/
+
+    const uint8_t* pixelPointer[] = { reinterpret_cast<const uint8_t*>(frame.pixels.get()), 0 };
+    const int linesizeIn[] = { frame.width * sizeof(RGBColor) };
+
+    sws_scale(swsContext, pixelPointer, linesizeIn, 0,
+        frame.height, picture->data, picture->linesize);
+
+    picture->pts = frameIndex++;
+
+    /* encode the image */
+    encode(codecContext, picture, pkt, file);
+}

+ 39 - 0
VideoStream.h

@@ -0,0 +1,39 @@
+#pragma once
+#ifndef VIDEO_STREAM_H_
+#define VIDEO_STREAM_H_
+
+#include <string>
+#include "Bitmap.h"
+
+extern "C" {
+#   include <libavformat/avformat.h>
+#   include <libavformat/avio.h>
+#   include <libavcodec/avcodec.h>
+#   include <libavformat/avformat.h>
+#   include <libavutil/imgutils.h>
+#   include <libavutil/opt.h>
+#   include <libswscale/swscale.h>
+}
+
+class VideoStream
+{
+    const AVCodec* codec;
+    AVCodecContext* codecContext;
+    FILE* file;
+    AVFrame* picture;
+    AVPacket* pkt;
+    SwsContext* swsContext;
+    static const uint8_t endcode[];
+
+    size_t width;
+    size_t height;
+
+    uint64_t frameIndex = 0;
+public:
+    VideoStream(::size_t width, ::size_t height, const std::string& filename);
+    ~VideoStream(void);
+
+    void addFrame(const Bitmap<RGBColor>& frame);
+};
+
+#endif // VIDEO_STREAM_H_

+ 55 - 0
benchmarkdialog.cpp

@@ -0,0 +1,55 @@
+#include "benchmarkdialog.h"
+#include <chrono>
+
+BenchmarkDialog::BenchmarkDialog(QWidget *parent) : QDialog(parent)
+{
+    ui.setupUi(this);
+}
+
+MandelViewport BenchmarkDialog::benchViewport(void) const
+{
+    return MandelViewport{ -0.758267525104592591494, -0.066895616551111110830, 0.000000043217777777655, 0.000000043217777777655 };
+}
+
+
+double BenchmarkDialog::measureMips(const std::function<Bitmap<float>()>& bench) const
+{
+    using namespace std::chrono;
+    time_point before = high_resolution_clock::now();
+    auto bitmap = bench();
+    time_point after = high_resolution_clock::now();
+
+    long long sum = 0;
+    for (int i = 0; i < bitmap.width * bitmap.height; i++) {
+        sum += std::floor(bitmap.pixels[size_t(i)]);
+    }
+
+    double iterPerNanos = double(sum) / duration_cast<nanoseconds>(after - before).count();
+    printf("test took %lld nanos\n", duration_cast<nanoseconds>(after - before).count());
+    printf("test did %lld iters\n", sum);
+    double megaItersPerSecond = iterPerNanos * 1000.0;
+    return megaItersPerSecond;
+}
+
+QString BenchmarkDialog::benchmarkResult(MandelGenerator& mg, int size, int iters) const
+{
+    MandelInfo mi;
+    mi.bWidth = size;
+    mi.bHeight = size;
+    mi.maxIter = iters;
+    mi.view = benchViewport();
+    double megaItersPerSecond = measureMips([&mg, &mi] () { return mg.generateRaw(mi); });
+    QString mips = QString::number(megaItersPerSecond, 'f', 2);
+    return mips;
+}
+
+void BenchmarkDialog::on_run_clicked()
+{
+    CpuGenerator<double> cpg;
+    CpuGenerator<float> cpgf;
+    ClGenerator clg;
+
+    ui.tableWidget->setItem(1, 0, new QTableWidgetItem(benchmarkResult(cpg, 2500, 5000)));
+    ui.tableWidget->setItem(0, 0, new QTableWidgetItem(benchmarkResult(cpgf, 2500, 5000)));
+    ui.tableWidget->setItem(0, 1, new QTableWidgetItem(benchmarkResult(clg, 4000, 8000)));
+}

+ 29 - 0
benchmarkdialog.h

@@ -0,0 +1,29 @@
+#ifndef BENCHMARKDIALOG_H
+#define BENCHMARKDIALOG_H
+
+#include <QDialog>
+#include <functional>
+#include "ui_benchmarks.h"
+#include "Generators.h"
+
+class BenchmarkDialog : public QDialog
+{
+    Q_OBJECT
+private:
+    Ui::BenchmarkDialog ui;
+public:
+    explicit BenchmarkDialog(QWidget *parent = nullptr);
+
+    MandelViewport benchViewport(void) const;
+
+    double measureMips(const std::function<Bitmap<float>()>& bench) const;
+    QString benchmarkResult(MandelGenerator& mg, int size, int iters) const;
+
+signals:
+
+public slots:
+private slots:
+    void on_run_clicked();
+};
+
+#endif // BENCHMARKDIALOG_H

+ 94 - 0
benchmarks.ui

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BenchmarkDialog</class>
+ <widget class="QDialog" name="BenchmarkDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>416</width>
+    <height>312</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="Line" name="line_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>All results are measured in mega-iterations per second.</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Line" name="line">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="run">
+       <property name="text">
+        <string>Run</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QTableWidget" name="tableWidget">
+       <property name="editTriggers">
+        <set>QAbstractItemView::NoEditTriggers</set>
+       </property>
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="rowCount">
+        <number>3</number>
+       </property>
+       <property name="columnCount">
+        <number>2</number>
+       </property>
+       <row>
+        <property name="text">
+         <string>Single precision</string>
+        </property>
+       </row>
+       <row>
+        <property name="text">
+         <string>Double precision</string>
+        </property>
+       </row>
+       <row>
+        <property name="text">
+         <string>128-bit fixed-point</string>
+        </property>
+       </row>
+       <column>
+        <property name="text">
+         <string>CPU</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>GPU</string>
+        </property>
+       </column>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 132 - 0
exportimagedialog.ui

@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ExportImageDialog</class>
+ <widget class="QDialog" name="ExportImageDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>184</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <widget class="QDialogButtonBox" name="buttonBox">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>140</y>
+     <width>341</width>
+     <height>32</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+   <property name="standardButtons">
+    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+   </property>
+  </widget>
+  <widget class="QWidget" name="formLayoutWidget">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>10</y>
+     <width>371</width>
+     <height>121</height>
+    </rect>
+   </property>
+   <layout class="QFormLayout" name="formLayout">
+    <item row="0" column="0">
+     <widget class="QLabel" name="label">
+      <property name="text">
+       <string>Maximum iterations</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="1">
+     <widget class="QLineEdit" name="maxIterations">
+      <property name="text">
+       <string>5000</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="0">
+     <widget class="QLabel" name="label_2">
+      <property name="text">
+       <string>Image Width</string>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="0">
+     <widget class="QLabel" name="label_3">
+      <property name="text">
+       <string>Image Height</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="1">
+     <widget class="QLineEdit" name="imgWidth">
+      <property name="text">
+       <string>1920</string>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="1">
+     <widget class="QLineEdit" name="imgHeight">
+      <property name="text">
+       <string>1080</string>
+      </property>
+     </widget>
+    </item>
+    <item row="3" column="0">
+     <widget class="QPushButton" name="pushButton">
+      <property name="text">
+       <string>Save As</string>
+      </property>
+     </widget>
+    </item>
+    <item row="3" column="1">
+     <widget class="QLineEdit" name="savePath"/>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ExportImageDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ExportImageDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 2 - 0
fdgsdfg.qrc

@@ -0,0 +1,2 @@
+<!DOCTYPE RCC>
+<RCC version="1.0"/>

+ 10 - 0
main.cpp

@@ -0,0 +1,10 @@
+#include "Almond.h"
+#include <QtWidgets/QApplication>
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    Almond w;
+    w.show();
+    return a.exec();
+}