Bladeren bron

reworking widget

Nicolas Winkler 5 jaren geleden
bovenliggende
commit
f84a52554f
5 gewijzigde bestanden met toevoegingen van 239 en 31 verwijderingen
  1. 2 2
      Almond.ui
  2. 110 23
      MandelWidget.cpp
  3. 78 6
      MandelWidget.h
  4. 1 0
      libmandel/CMakeLists.txt
  5. 48 0
      libmandel/src/CpuGenerators.cpp

+ 2 - 2
Almond.ui

@@ -9,8 +9,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1119</width>
-    <height>682</height>
+    <width>666</width>
+    <height>563</height>
    </rect>
   </property>
   <property name="windowTitle">

+ 110 - 23
MandelWidget.cpp

@@ -59,7 +59,25 @@ Texture::Texture(const Bitmap<RGBColor>& bitmap, QOpenGLContext* context) :
 
 Texture::~Texture(void)
 {
-    glDeleteTextures(1, &id);
+    if (id != 0)
+        glDeleteTextures(1, &id);
+}
+
+
+Texture::Texture(Texture&& other) :
+    id{ other.id },
+    context{ other.context }
+{
+    other.id = 0;
+}
+
+
+Texture& Texture::operator=(Texture&& other)
+{
+    this->id = other.id;
+    this->context = other.context;
+    other.id = 0;
+    return *this;
 }
 
 
@@ -90,13 +108,13 @@ void Texture::drawRect(float x, float y, float width, float height)
 
 std::pair<int, int> TexGrid::getCellIndices(double x, double y)
 {
-    return { int(x / dpp / 64), int(y / dpp / 64) };
+    return { ::floor(x / dpp / MandelV::chunkSize), ::floor(y / dpp / MandelV::chunkSize) };
 }
 
 
 std::pair<double, double> TexGrid::getPositions(int x, int y)
 {
-    return { x * dpp * 64, y * dpp * 64 };
+    return { x * dpp * MandelV::chunkSize, y * dpp * MandelV::chunkSize };
 }
 
 
@@ -104,7 +122,7 @@ Texture* TexGrid::getCell(int i, int j)
 {
     auto cIt = cells.find({i, j});
     if (cIt != cells.end()) {
-        return &cIt->second;
+        return cIt->second.get();
     }
     else {
         return nullptr;
@@ -112,8 +130,60 @@ Texture* TexGrid::getCell(int i, int j)
 }
 
 
-MandelV::MandelV(QOpenGLContext* context) :
-    empty{ Bitmap<RGBColor>(1, 1) }
+void TexGrid::setCell(int i, int j, std::unique_ptr<Texture> tex)
+{
+    cells[{i, j}] = std::move(tex);
+}
+
+
+void TexGrid::clearCells(void)
+{
+    cells.clear();
+}
+
+
+void Job::run(void)
+{
+    auto [absX, absY] = grid->getPositions(i, j);
+    double gw = grid->dpp * MandelV::chunkSize;
+
+    Bitmap<float> f(MandelV::chunkSize, MandelV::chunkSize);
+    mnd::MandelInfo mi;
+    mi.view.x = absX;
+    mi.view.y = absY;
+    mi.view.width = mi.view.height = gw;
+    mi.bWidth = mi.bHeight = MandelV::chunkSize;
+    mi.maxIter = 500;
+    mndContext.getDefaultGenerator().generate(mi, f.pixels.get());
+    Bitmap<RGBColor>* rgb = new Bitmap<RGBColor>(f.map<RGBColor>([] (float f) {
+        return RGBColor{ uint8_t(f / 2), uint8_t(f / 2), uint8_t(f / 2) };
+    }));
+    emit done(level, i, j, rgb);
+}
+
+
+void Calcer::calc(TexGrid& grid, int level, int i, int j)
+{
+    if (jobs.find({ level, i, j }) == jobs.end()) {
+        Job* job = new Job(mndContext, &grid, level, i, j);
+        connect(job, &Job::done, this, &Calcer::redirect);
+        jobs.insert({ level, i, j });
+        //jobs.push_back(std::move(job));
+        threadPool->start(job);
+    }
+}
+
+
+void Calcer::redirect(int level, int i, int j, Bitmap<RGBColor>* bmp)
+{
+    jobs.erase({ level, i, j });
+    emit done(level, i, j, bmp);
+}
+
+
+MandelV::MandelV(mnd::MandelContext& mndContext) :
+    mndContext{ mndContext },
+    calcThread{ std::make_unique<Calcer>(mndContext) }
 {
     Bitmap<RGBColor> emp(8, 8);
     for(auto i = 0; i < emp.width; i++) {
@@ -126,19 +196,19 @@ MandelV::MandelV(QOpenGLContext* context) :
             }
         }
     }
-    context->makeCurrent(nullptr);
-    empty = Texture(emp, context);
+    empty = std::make_unique<Texture>(emp);
+    connect(calcThread.get(), &Calcer::done, this, &MandelV::cellReady);
 }
 
 
 int MandelV::getLevel(double dpp) {
-    return -int(::log2(dpp / 64));
+    return -int(::log2(dpp / chunkSize));
 }
 
 
 double MandelV::getDpp(int level)
 {
-    return ::pow(2, -level) * 64;
+    return ::pow(2, -level) * chunkSize;
 }
 
 
@@ -156,14 +226,11 @@ TexGrid& MandelV::getGrid(int level)
 
 void MandelV::paint(const mnd::MandelViewport& mvp)
 {
-    this->empty.drawRect(0, 0, 100, 100);
-    return;
-    int width = 1024;
     double dpp = mvp.width / width;
     int level = getLevel(dpp);
 
     auto& grid = getGrid(level);
-    double gw = getDpp(level) * 64;
+    double gw = getDpp(level) * chunkSize;
 
     auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
     auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
@@ -172,20 +239,30 @@ void MandelV::paint(const mnd::MandelViewport& mvp)
 
             auto [absX, absY] = grid.getPositions(i, j);
             double x = (absX - mvp.x) * width / mvp.width;
-            double y = (absY - mvp.y) * width / mvp.height;
-            double w = width / mvp.width * gw;
+            double y = (absY - mvp.y) * height / mvp.height;
+            double w = width * gw / mvp.width;
+            double h = height * gw / mvp.height;
 
             Texture* t = grid.getCell(i, j);
             if (t != nullptr) {
                 t->drawRect(x, y, w, w);
             }
             else {
-                this->empty.drawRect(x, y, w, w);
+                calcThread->calc(grid, level, i, j);
+                this->empty->drawRect(x, y, w, w);
             }
         }
     }
 }
 
+void MandelV::cellReady(int level, int i, int j, Bitmap<RGBColor>* bmp)
+{
+    this->getGrid(level).setCell(i, j, std::make_unique<Texture>(*bmp));
+    delete bmp;
+    printf("cellReady: %d --> %d, %d\n", level, i, j);
+    emit redrawRequested();
+}
+
 
 MandelView::MandelView(mnd::Generator& generator, Gradient &gradient, MandelWidget* mWidget) :
     generator{ &generator },
@@ -343,11 +420,12 @@ MandelWidget::~MandelWidget()
 void MandelWidget::initializeGL(void)
 {
     this->context()->functions()->glClearColor(0, 0, 0, 0);
+    this->context()->makeCurrent(nullptr);
 
     glDisable(GL_DEPTH_TEST);
 
     // looks not even better
-    glDisable(GL_FRAMEBUFFER_SRGB);
+    //glDisable(GL_FRAMEBUFFER_SRGB);
 
     //glShadeModel(GL_SMOOTH);
 
@@ -360,16 +438,22 @@ void MandelWidget::initializeGL(void)
     auto bitmap = cpg.generate(mi);*/
     Bitmap<RGBColor> bitmap(1, 1);
     bitmap.get(0, 0) = RGBColor{50, 50, 50};
-    v = std::make_unique<MandelV>(context());
+    v = nullptr;
 
     tex = std::make_unique<Texture>(bitmap, context());
     mv.start();
     requestRecalc();
+
+
 }
 
 
 void MandelWidget::paintGL(void)
 {
+    if (v == nullptr) {
+        v = std::make_unique<MandelV>(mndContext);
+        QObject::connect(v.get(), &MandelV::redrawRequested, this, static_cast<void(QOpenGLWidget::*)(void)>(&QOpenGLWidget::update));
+    }
     /*if (!initialized) {
         emit needsUpdate(viewport);
         initialized = true;
@@ -377,7 +461,10 @@ void MandelWidget::paintGL(void)
 
     int width = this->width();
     int height = this->height();
+    v->width = width;
+    v->height = height;
 
+    //v = std::make_unique<MandelV>(context());
     /*CpuGenerator<double> cpg;
     ClGenerator clg;
     MandelGenerator& mg = cpg;
@@ -405,14 +492,13 @@ void MandelWidget::paintGL(void)
 
     glClear(GL_COLOR_BUFFER_BIT);
     glLoadIdentity();
-    tex->drawRect(0, 0, width, height);
+    //tex->drawRect(0, 0, width, height);
 
     //v->empty = std::move(*tex)
     //v->empty.bind();
     v->paint(this->viewport);
     //*tex = std::move(v->empty);
 
-
     if (rubberbandDragging)
         drawRubberband();
 
@@ -448,13 +534,14 @@ void MandelWidget::setMaxIterations(int maxIter)
 
 void MandelWidget::requestRecalc()
 {
-    emit needsUpdate(MandelInfo{ viewport, this->width(), this->height(), maxIterations });
+    //emit needsUpdate(MandelInfo{ viewport, this->width(), this->height(), maxIterations });
+    this->update();
 }
 
 
 void MandelWidget::resizeGL(int width, int height)
 {
-    glViewport(0, 0, (GLint) width, (GLint) height);
+    //glViewport(0, 0, (GLint) width, (GLint) height);
 }
 
 

+ 78 - 6
MandelWidget.h

@@ -3,6 +3,7 @@
 #include <QGLWidget>
 #include <QOpenGLWidget>
 #include <QThread>
+#include <QThreadPool>
 #include <qopengl.h>
 #include <qopenglfunctions.h>
 #include <qopenglcontext.h>
@@ -19,7 +20,10 @@
 #include <thread>
 #include <mutex>
 #include <atomic>
+#include <tuple>
+#include <deque>
 #include <unordered_map>
+#include <unordered_set>
 
 class MandelWidget;
 
@@ -35,8 +39,8 @@ public:
     Texture(const Texture& other) = delete;
     Texture& operator=(const Texture& other) = delete;
 
-    Texture(Texture&& other) = default;
-    Texture& operator=(Texture&& other) = default;
+    Texture(Texture&& other);
+    Texture& operator=(Texture&& other);
 
     void bind(void) const;
     inline GLuint getId(void) const { return id; }
@@ -59,33 +63,100 @@ struct PairHash {
     }
 };
 
+struct TripleHash {
+    template <typename T1, typename T2, typename T3>
+    std::size_t operator () (const std::tuple<T1, T2, T3>& p) const {
+        auto h1 = std::hash<T1>{}(std::get<0>(p));
+        auto h2 = std::hash<T2>{}(std::get<1>(p));
+        auto h3 = std::hash<T3>{}(std::get<2>(p));
+        return (((h1 ^ 234579245) * 23452357 + h2) ^ 2345244345) * 23421 + h3;
+    }
+};
+
 
 class TexGrid
 {
+public:
     double dpp;
-    std::unordered_map<std::pair<int, int>, Texture, PairHash> cells;
+    std::unordered_map<std::pair<int, int>, std::unique_ptr<Texture>, PairHash> cells;
 public:
     inline TexGrid(void) : dpp{ 1.0 } {}
     inline TexGrid(double dpp) : dpp{ dpp } {}
     std::pair<int, int> getCellIndices(double x, double y);
     std::pair<double, double> getPositions(int i, int j);
     Texture* getCell(int i, int j);
+    void setCell(int i, int j, std::unique_ptr<Texture> tex);
+
+    void clearCells(void);
+};
+
+
+class Job :  public QObject, public QRunnable
+{
+    Q_OBJECT
+public:
+    mnd::MandelContext& mndContext;
+    TexGrid* grid;
+    int level;
+    int i, j;
+
+    inline Job(mnd::MandelContext& mndContext, TexGrid* grid, int level, int i, int j) :
+        mndContext{ mndContext },
+        grid{ grid },
+        level{ level },
+        i{ i }, j{ j }
+    {}
+
+    void run() override;
+signals:
+    void done(int level, int i, int j, Bitmap<RGBColor>* bmp);
+};
+
+
+class Calcer : public QObject
+{
+    Q_OBJECT
+    std::unordered_set<std::tuple<int, int, int>, TripleHash> jobs;
+    mnd::MandelContext& mndContext;
+    std::unique_ptr<QThreadPool> threadPool;
+public:
+    inline Calcer(mnd::MandelContext& mc) :
+        mndContext{ mc },
+        threadPool{ std::make_unique<QThreadPool>() }
+    {
+    }
+
+public slots:
+    void calc(TexGrid& grid, int level, int i, int j);
+    void redirect(int level, int i, int j, Bitmap<RGBColor>* bmp);
+signals:
+    void done(int level, int i, int j, Bitmap<RGBColor>* bmp);
 };
 
 
-class MandelV
+class MandelV : public QObject
 {
+    Q_OBJECT
 public:
-    Texture empty;
+    std::unique_ptr<Texture> empty;
     std::unordered_map<int, TexGrid> levels;
+    mnd::MandelContext& mndContext;
+    std::unique_ptr<Calcer> calcThread;
+    int width;
+    int height;
 public:
-    MandelV(QOpenGLContext* context);
+    static const int chunkSize = 128;
+    MandelV(mnd::MandelContext& mndContext);
     int getLevel(double dpp);
     double getDpp(int level);
 
     TexGrid& getGrid(int level);
 
     void paint(const mnd::MandelViewport& mvp);
+public slots:
+    void cellReady(int level, int i, int j, Bitmap<RGBColor>* bmp);
+signals:
+    void redrawRequested(void);
 };
 
 
@@ -120,6 +191,7 @@ signals:
     void updated(Bitmap<RGBColor>* bitmap);
 };
 
+
 class MandelWidget : public QOpenGLWidget
 {
     Q_OBJECT

+ 1 - 0
libmandel/CMakeLists.txt

@@ -22,6 +22,7 @@ find_library(MPFR_LIBRARIES mpfr PATHS $ENV{MPFR_DIR} ${LIB_INSTALL_DIR})
 
 if(MPFR_INCLUDES AND MPFR_LIBRARIES)
     set(MPFR_FOUND TRUE)
+    target_compile_definitions(mandel PUBLIC WITH_MPFR)
 endif()
 
 set(CMAKE_CXX_STANDARD 17)

+ 48 - 0
libmandel/src/CpuGenerators.cpp

@@ -1,6 +1,10 @@
 #include "CpuGenerators.h"
 #include "Fixed.h"
 
+#ifdef WITH_MPFR
+#include "MpfrWrapper.h"
+#endif // WITH_MPFR
+
 #include <omp.h>
 #include <cmath>
 
@@ -65,6 +69,50 @@ void CpuGenerator<T, mnd::NONE, parallel, smooth>::generate(const mnd::MandelInf
 }
 
 
+#ifdef WITH_MPFR
+template<unsigned int bits, bool parallel, bool smooth>
+void CpuGenerator<mnd::MpfrFloat<bits>, mnd::NONE, parallel, smooth>::generate(const mnd::MandelInfo& info, float* data)
+{
+    const MandelViewport& view = info.view;
+    using T = mnd::MpfrFloat<bits>;
+
+    if constexpr (parallel)
+        omp_set_num_threads(2 * omp_get_num_procs());
+#pragma omp parallel for if (parallel)
+    for (long j = 0; j < info.bHeight; j++) {
+        T y = T(view.y) + T(j) * T(view.height / info.bHeight);
+        long i = 0;
+        for (i; i < info.bWidth; i++) {
+            T x = T(view.x + T(i) * T(view.width / info.bWidth));
+
+            T a = x;
+            T b = y;
+
+            int k = 0;
+            for (k = 0; k < info.maxIter; k++) {
+                T aa = a * a;
+                T bb = b * b;
+                T ab = a * b;
+                a = aa - bb + x;
+                b = ab + ab + y;
+                if (aa + bb > T(16)) {
+                    break;
+                }
+            }
+            if constexpr (smooth) {
+                if (k >= info.maxIter)
+                    data[i + j * info.bWidth] = info.maxIter;
+                else
+                    data[i + j * info.bWidth] = ((float) k) + 1 - ::log(::log(a * a + b * b) / 2) / ::log(2.0f);
+            }
+            else
+                data[i + j * info.bWidth] = k;
+        }
+    }
+}
+#endif // WITH_MPFR
+
+
 /*
 void CpuGeneratorDouble::generate(const mnd::MandelInfo& info, float* data)
 {