Nicolas Winkler 5 年之前
父節點
當前提交
3ae18d9bdd
共有 3 個文件被更改,包括 107 次插入37 次删除
  1. 59 26
      MandelWidget.cpp
  2. 35 9
      MandelWidget.h
  3. 13 2
      libmandel/src/mandel.cpp

+ 59 - 26
MandelWidget.cpp

@@ -6,7 +6,7 @@ using namespace mnd;
 #include <QOpenGLVertexArrayObject>
 
 
-Texture::Texture(const Bitmap<RGBColor>& bitmap) :
+Texture::Texture(const Bitmap<RGBColor>& bitmap, GLint param) :
     context{ nullptr }
 {
     glGenTextures(1, &id);
@@ -27,8 +27,8 @@ Texture::Texture(const Bitmap<RGBColor>& bitmap) :
     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);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param);
 }
 
 Texture::Texture(const Bitmap<RGBColor>& bitmap, QOpenGLContext* context) :
@@ -153,23 +153,36 @@ void Job::run(void)
     mi.view.y = absY;
     mi.view.width = mi.view.height = gw;
     mi.bWidth = mi.bHeight = MandelV::chunkSize;
-    mi.maxIter = 500;
+    mi.maxIter = maxIter;
     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) };
+    auto* rgb = new Bitmap<RGBColor>(f.map<RGBColor>([&mi, this](float i) {
+        return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : gradient.get(i);
     }));
     emit done(level, i, j, rgb);
 }
 
 
+void Calcer::setMaxIter(int maxIter)
+{
+    this->maxIter = maxIter;
+    clearAll();
+}
+
+
+void Calcer::clearAll(void)
+{
+    this->threadPool->clear();
+}
+
+
 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 });
+        auto job = std::make_unique<Job>(mndContext, gradient, maxIter, &grid, level, i, j);
+        connect(job.get(), &Job::done, this, &Calcer::redirect);
+        jobs.insert({ std::tuple{ level, i, j }, std::move(job) });
         //jobs.push_back(std::move(job));
-        threadPool->start(job);
+        threadPool->start(job.get());
     }
 }
 
@@ -181,9 +194,11 @@ void Calcer::redirect(int level, int i, int j, Bitmap<RGBColor>* bmp)
 }
 
 
-MandelV::MandelV(mnd::MandelContext& mndContext) :
+MandelV::MandelV(mnd::MandelContext& mndContext, Gradient& gradient, int maxIter) :
     mndContext{ mndContext },
-    calcThread{ std::make_unique<Calcer>(mndContext) }
+    calcer{ mndContext, gradient, maxIter },
+    gradient{ gradient },
+    maxIter{ maxIter }
 {
     Bitmap<RGBColor> emp(8, 8);
     for(auto i = 0; i < emp.width; i++) {
@@ -196,27 +211,28 @@ MandelV::MandelV(mnd::MandelContext& mndContext) :
             }
         }
     }
-    empty = std::make_unique<Texture>(emp);
-    connect(calcThread.get(), &Calcer::done, this, &MandelV::cellReady);
+    empty = std::make_unique<Texture>(emp, GL_NEAREST);
+    connect(&calcer, &Calcer::done, this, &MandelV::cellReady);
 }
 
 
 int MandelV::getLevel(double dpp) {
-    return -int(::log2(dpp / chunkSize));
+    return int(::log2(dpp / chunkSize));
 }
 
 
 double MandelV::getDpp(int level)
 {
-    return ::pow(2, -level) * chunkSize;
+    return ::pow(2, level) * chunkSize;
 }
 
 
 TexGrid& MandelV::getGrid(int level)
 {
     auto it = levels.find(level);
-    if (it != levels.end())
+    if (it != levels.end()) {
         return it->second;
+    }
     else {
         levels.insert({ level, TexGrid(getDpp(level)) });
         return levels[level];
@@ -224,6 +240,22 @@ TexGrid& MandelV::getGrid(int level)
 }
 
 
+void MandelV::setMaxIter(int maxIter)
+{
+    this->maxIter = maxIter;
+    calcer.setMaxIter(maxIter);
+}
+
+
+
+void MandelV::clear(void)
+{
+    for(auto[level, grid] : this->levels) {
+        grid.clearCells();
+    }
+}
+
+
 void MandelV::garbageCollect(int level)
 {
     for(auto& [l, grid] : levels) {
@@ -232,19 +264,19 @@ void MandelV::garbageCollect(int level)
             grid.clearCells();
         }
         else if (dist > 10) {
-            if (grid.countAllocatedCells() > 20)
+            if (grid.countAllocatedCells() > 50)
                 grid.clearCells();
         }
         else if (dist > 3) {
-            if (grid.countAllocatedCells() > 80)
+            if (grid.countAllocatedCells() > 150)
                 grid.clearCells();
         }
         else if (dist > 0) {
-            if (grid.countAllocatedCells() > 150)
+            if (grid.countAllocatedCells() > 350)
                 grid.clearCells();
         }
         else {
-            if (grid.countAllocatedCells() > 250)
+            if (grid.countAllocatedCells() > 2500)
                 grid.clearCells();
         }
     }
@@ -254,7 +286,7 @@ void MandelV::garbageCollect(int level)
 void MandelV::paint(const mnd::MandelViewport& mvp)
 {
     double dpp = mvp.width / width;
-    int level = getLevel(dpp);
+    int level = getLevel(dpp) - 1;
     garbageCollect(level);
 
     auto& grid = getGrid(level);
@@ -262,6 +294,7 @@ void MandelV::paint(const mnd::MandelViewport& mvp)
 
     double w = width * gw / mvp.width;
     //double h = height * gw / mvp.height;
+    printf("level: %d, dpp: %f, width: %f\n", level, dpp, w);
 
     auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
     auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
@@ -284,7 +317,7 @@ void MandelV::paint(const mnd::MandelViewport& mvp)
                 glEnd();*/
             }
             else {
-                calcThread->calc(grid, level, i, j);
+                calcer.calc(grid, level, i, j);
                 this->empty->drawRect(x, y, w, w);
             }
         }
@@ -486,7 +519,7 @@ void MandelWidget::initializeGL(void)
 void MandelWidget::paintGL(void)
 {
     if (v == nullptr) {
-        v = std::make_unique<MandelV>(mndContext);
+        v = std::make_unique<MandelV>(mndContext, gradient, maxIterations);
         QObject::connect(v.get(), &MandelV::redrawRequested, this, static_cast<void(QOpenGLWidget::*)(void)>(&QOpenGLWidget::update));
     }
     /*if (!initialized) {
@@ -536,8 +569,6 @@ void MandelWidget::paintGL(void)
 
     if (rubberbandDragging)
         drawRubberband();
-
-    printf("painted GL\n");
 }
 
 
@@ -563,6 +594,8 @@ void MandelWidget::zoom(float scale)
 void MandelWidget::setMaxIterations(int maxIter)
 {
     this->maxIterations = maxIter;
+    if (v)
+        v->setMaxIter(maxIter);
     requestRecalc();
 }
 

+ 35 - 9
MandelWidget.h

@@ -32,7 +32,7 @@ class Texture
     GLuint id;
     QOpenGLContext* context;
 public:
-    Texture(const Bitmap<RGBColor>& pict);
+    Texture(const Bitmap<RGBColor>& pict, GLint param = GL_LINEAR);
     Texture(const Bitmap<RGBColor>& pict, QOpenGLContext* context);
     ~Texture(void);
 
@@ -92,17 +92,25 @@ public:
 };
 
 
-class Job :  public QObject, public QRunnable
+class Job : public QObject, public QRunnable
 {
     Q_OBJECT
 public:
     mnd::MandelContext& mndContext;
+    Gradient& gradient;
+    int maxIter;
     TexGrid* grid;
     int level;
     int i, j;
 
-    inline Job(mnd::MandelContext& mndContext, TexGrid* grid, int level, int i, int j) :
+    inline Job(mnd::MandelContext& mndContext,
+               Gradient& gradient,
+               int maxIter,
+               TexGrid* grid,
+               int level, int i, int j) :
         mndContext{ mndContext },
+        gradient{ gradient },
+        maxIter{ maxIter },
         grid{ grid },
         level{ level },
         i{ i }, j{ j }
@@ -117,16 +125,24 @@ signals:
 class Calcer : public QObject
 {
     Q_OBJECT
-    std::unordered_set<std::tuple<int, int, int>, TripleHash> jobs;
+    std::unordered_map<std::tuple<int, int, int>, std::unique_ptr<Job>, TripleHash> jobs;
     mnd::MandelContext& mndContext;
     std::unique_ptr<QThreadPool> threadPool;
+    Gradient& gradient;
+    int maxIter;
 public:
-    inline Calcer(mnd::MandelContext& mc) :
+    inline Calcer(mnd::MandelContext& mc, Gradient& gradient, int maxIter) :
         mndContext{ mc },
-        threadPool{ std::make_unique<QThreadPool>() }
+        threadPool{ std::make_unique<QThreadPool>() },
+        gradient{ gradient },
+        maxIter{ maxIter }
     {
+        threadPool->setMaxThreadCount(4);
     }
 
+    void setMaxIter(int maxIter);
+    void clearAll(void);
+
 public slots:
     void calc(TexGrid& grid, int level, int i, int j);
     void redirect(int level, int i, int j, Bitmap<RGBColor>* bmp);
@@ -140,19 +156,29 @@ class MandelV : public QObject
     Q_OBJECT
 public:
     std::unique_ptr<Texture> empty;
+
+    // a grid should not be deleted once constructed.
+    // to free up memory one can call TexGrid::clearCells()
     std::unordered_map<int, TexGrid> levels;
     mnd::MandelContext& mndContext;
-    std::unique_ptr<Calcer> calcThread;
+    Calcer calcer;
+    Gradient& gradient;
+    int maxIter;
     int width;
     int height;
 public:
-    static const int chunkSize = 128;
-    MandelV(mnd::MandelContext& mndContext);
+    static const int chunkSize = 256;
+    MandelV(mnd::MandelContext& mndContext, Gradient& gradient, int maxIter);
     int getLevel(double dpp);
     double getDpp(int level);
 
     TexGrid& getGrid(int level);
 
+    inline int getMaxIter(void) const { return this->maxIter; }
+    void setMaxIter(int maxIter);
+
+    void clear(void);
+
     void garbageCollect(int level);
     void paint(const mnd::MandelViewport& mvp);
 public slots:

+ 13 - 2
libmandel/src/mandel.cpp

@@ -80,9 +80,20 @@ MandelContext::MandelContext(void)
     cpuGenerator128 = std::make_unique<CpuGenerator<Fixed128>>();
     //cpuGeneratorFixedp = std::make_unique<CpuGenerator<fixed<1, 3>>>();
 
-    adaptiveGenerator = std::make_unique<AdaptiveGenerator>(cpuGeneratorFloat.get(), cpuGeneratorDouble.get());
-
     devices = createDevices();
+    if (devices.empty()) {
+        adaptiveGenerator = std::make_unique<AdaptiveGenerator>(cpuGeneratorFloat.get(), cpuGeneratorDouble.get());
+    }
+    else {
+        auto& device1 = devices[0];
+        Generator* floatGenerator = device1.getGeneratorFloat();
+        Generator* doubleGenerator = device1.getGeneratorDouble();
+        if (floatGenerator == nullptr)
+            floatGenerator = cpuGeneratorFloat.get();
+        if (doubleGenerator == nullptr)
+            doubleGenerator = cpuGeneratorDouble.get();
+        adaptiveGenerator = std::make_unique<AdaptiveGenerator>(floatGenerator, doubleGenerator);
+    }
 }