summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Kintel <marius@kintel.net>2011-12-25 22:00:30 (GMT)
committerMarius Kintel <marius@kintel.net>2011-12-25 22:00:30 (GMT)
commit7c48b345b12981085bf6741208893a8206d77578 (patch)
treedec80733c559903f6c463a3827e4bf2e2cea562e
parent3e64e63b0113a99666ad68aa3e82bb7b80324d9b (diff)
Perform CGAL evaluation in a separate thread. First steps towards better GUI responsiveness and parallelization
-rw-r--r--openscad.pro6
-rw-r--r--src/MainWindow.h17
-rw-r--r--src/MainWindow.ui6
-rw-r--r--src/ProgressWidget.cc12
-rw-r--r--src/ProgressWidget.h5
-rw-r--r--src/cgalworker.cc40
-rw-r--r--src/cgalworker.h28
-rw-r--r--src/mainwin.cc181
8 files changed, 173 insertions, 122 deletions
diff --git a/openscad.pro b/openscad.pro
index 50a419d..594e890 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -249,7 +249,8 @@ HEADERS += src/cgal.h \
src/CGALCache.h \
src/PolySetCGALEvaluator.h \
src/CGALRenderer.h \
- src/CGAL_Nef_polyhedron.h
+ src/CGAL_Nef_polyhedron.h \
+ src/cgalworker.h
SOURCES += src/cgalutils.cc \
src/CGALEvaluator.cc \
@@ -258,7 +259,8 @@ SOURCES += src/cgalutils.cc \
src/CGALRenderer.cc \
src/CGAL_Nef_polyhedron.cc \
src/CGAL_Nef_polyhedron_DxfData.cc \
- src/cgaladv_minkowski2.cc
+ src/cgaladv_minkowski2.cc \
+ src/cgalworker.cc
}
macx {
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 0f2a922..577209f 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -9,6 +9,7 @@
#include "Tree.h"
#include "memory.h"
#include <vector>
+#include <QMutex>
class MainWindow : public QMainWindow, public Ui::MainWindow
{
@@ -67,9 +68,7 @@ private slots:
void updateTVal();
void setFileName(const QString &filename);
void setFont(const QString &family, uint size);
-#ifdef USE_PROGRESSWIDGET
void showProgress();
-#endif
private:
void openFile(const QString &filename);
@@ -80,9 +79,7 @@ private:
bool maybeSave();
bool checkModified();
QString dumpCSGTree(AbstractNode *root);
- static void consoleOutput(const std::string &msg, void *userdata) {
- static_cast<MainWindow*>(userdata)->console->append(QString::fromStdString(msg));
- }
+ static void consoleOutput(const std::string &msg, void *userdata);
void loadViewSettings();
void loadDesignSettings();
@@ -110,6 +107,7 @@ private slots:
void actionCompile();
#ifdef ENABLE_CGAL
void actionRenderCGAL();
+ void actionRenderCGALDone(class CGAL_Nef_polyhedron *);
#endif
void actionDisplayAST();
void actionDisplayCSGTree();
@@ -163,6 +161,13 @@ public slots:
void actionReloadCompile();
void checkAutoReload();
void autoReloadSet(bool);
+
+private:
+ static void report_func(const class AbstractNode*, void *vp, int mark);
+
+ class ProgressWidget *progresswidget;
+ class CGALWorker *cgalworker;
+ QMutex consolemutex;
};
class GuiLocker
@@ -175,6 +180,8 @@ public:
gui_locked--;
}
static bool isLocked() { return gui_locked > 0; }
+ static void lock() { gui_locked++; }
+ static void unlock() { gui_locked--; }
private:
static unsigned int gui_locked;
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index d1bdb00..f89e0d4 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -39,7 +39,11 @@
<enum>Qt::Vertical</enum>
</property>
<widget class="GLView" name="glview" native="true"/>
- <widget class="QTextEdit" name="console"/>
+ <widget class="QTextEdit" name="console">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
</widget>
</item>
<item>
diff --git a/src/ProgressWidget.cc b/src/ProgressWidget.cc
index 112e239..ce66405 100644
--- a/src/ProgressWidget.cc
+++ b/src/ProgressWidget.cc
@@ -5,7 +5,11 @@ ProgressWidget::ProgressWidget(QWidget *parent)
:QWidget(parent)
{
setupUi(this);
+ setRange(0, 100);
+ setValue(0);
this->wascanceled = false;
+ this->starttime.start();
+
connect(this->stopButton, SIGNAL(clicked()), this, SLOT(cancel()));
QTimer::singleShot(1000, this, SIGNAL(requestShow()));
}
@@ -15,6 +19,14 @@ bool ProgressWidget::wasCanceled() const
return this->wascanceled;
}
+/*!
+ Returns milliseconds since this widget was created
+*/
+int ProgressWidget::elapsedTime() const
+{
+ return this->starttime.elapsed();
+}
+
void ProgressWidget::cancel()
{
this->wascanceled = true;
diff --git a/src/ProgressWidget.h b/src/ProgressWidget.h
index 83e4d40..f610279 100644
--- a/src/ProgressWidget.h
+++ b/src/ProgressWidget.h
@@ -2,15 +2,17 @@
#define PROGRESSWIDGET_H_
#include "ui_ProgressWidget.h"
+#include <QTime>
class ProgressWidget : public QWidget, public Ui::ProgressWidget
{
Q_OBJECT;
Q_PROPERTY(bool wasCanceled READ wasCanceled);
-
+
public:
ProgressWidget(QWidget *parent = NULL);
bool wasCanceled() const;
+ int elapsedTime() const;
public slots:
void setRange(int minimum, int maximum);
@@ -23,6 +25,7 @@ signals:
private:
bool wascanceled;
+ QTime starttime;
};
#endif
diff --git a/src/cgalworker.cc b/src/cgalworker.cc
new file mode 100644
index 0000000..30bb1fe
--- /dev/null
+++ b/src/cgalworker.cc
@@ -0,0 +1,40 @@
+#include "cgalworker.h"
+#include <QThread>
+
+#include "Tree.h"
+#include "CGALEvaluator.h"
+#include "progress.h"
+#include "printutils.h"
+
+CGALWorker::CGALWorker()
+{
+ this->thread = new QThread();
+ connect(this->thread, SIGNAL(started()), this, SLOT(work()));
+ moveToThread(this->thread);
+}
+
+CGALWorker::~CGALWorker()
+{
+ delete this->thread;
+}
+
+void CGALWorker::start(const Tree &tree)
+{
+ this->tree = &tree;
+ this->thread->start();
+}
+
+void CGALWorker::work()
+{
+ CGAL_Nef_polyhedron *root_N = NULL;
+ try {
+ CGALEvaluator evaluator(*this->tree);
+ root_N = new CGAL_Nef_polyhedron(evaluator.evaluateCGALMesh(*this->tree->root()));
+ }
+ catch (ProgressCancelException e) {
+ PRINT("Rendering cancelled.");
+ }
+
+ emit done(root_N);
+ thread->quit();
+}
diff --git a/src/cgalworker.h b/src/cgalworker.h
new file mode 100644
index 0000000..cf60c24
--- /dev/null
+++ b/src/cgalworker.h
@@ -0,0 +1,28 @@
+#ifndef CGALWORKER_H_
+#define CGALWORKER_H_
+
+#include <QObject>
+
+class CGALWorker : public QObject
+{
+ Q_OBJECT;
+public:
+ CGALWorker();
+ virtual ~CGALWorker();
+
+public slots:
+ void start(const class Tree &tree);
+
+protected slots:
+ void work();
+
+signals:
+ void done(class CGAL_Nef_polyhedron *);
+
+protected:
+
+ class QThread *thread;
+ const class Tree *tree;
+};
+
+#endif
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 3243847..a3ec72f 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -41,9 +41,7 @@
#include "CSGTermEvaluator.h"
#include "OpenCSGRenderer.h"
#endif
-#ifdef USE_PROGRESSWIDGET
#include "ProgressWidget.h"
-#endif
#include "ThrownTogetherRenderer.h"
#include <QMenu>
@@ -52,8 +50,6 @@
#include <QSplitter>
#include <QFileDialog>
#include <QApplication>
-#include <QProgressDialog>
-#include <QProgressBar>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
@@ -67,6 +63,8 @@
#include <QMessageBox>
#include <QDesktopServices>
#include <QSettings>
+#include <QProgressDialog>
+#include <QMutexLocker>
#ifdef _QCODE_EDIT_
#include "qdocument.h"
#include "qformatscheme.h"
@@ -89,6 +87,7 @@ using namespace boost::lambda;
#include "CGALRenderer.h"
#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
+#include "cgalworker.h"
#endif // ENABLE_CGAL
@@ -140,9 +139,13 @@ settings_valueList(const QString &key, const QList<int> &defaultList = QList<int
}
MainWindow::MainWindow(const QString &filename)
+ : progresswidget(NULL)
{
setupUi(this);
+ this->cgalworker = new CGALWorker();
+ connect(this->cgalworker, SIGNAL(done(CGAL_Nef_polyhedron *)), this, SLOT(actionRenderCGALDone(CGAL_Nef_polyhedron *)));
+
register_builtin(root_ctx);
this->openglbox = NULL;
@@ -316,7 +319,6 @@ MainWindow::MainWindow(const QString &filename)
connect(this->helpActionOpenGLInfo, SIGNAL(triggered()), this, SLOT(helpOpenGL()));
- console->setReadOnly(true);
setCurrentOutput();
PRINT(helptitle);
@@ -421,32 +423,23 @@ MainWindow::~MainWindow()
#endif
}
-#ifdef USE_PROGRESSWIDGET
void MainWindow::showProgress()
{
this->statusBar()->addPermanentWidget(qobject_cast<ProgressWidget*>(sender()));
}
-#endif
-static void report_func(const class AbstractNode*, void *vp, int mark)
+void MainWindow::report_func(const class AbstractNode*, void *vp, int mark)
{
-#ifdef USE_PROGRESSWIDGET
- ProgressWidget *pw = static_cast<ProgressWidget*>(vp);
+ MainWindow *thisp = static_cast<MainWindow*>(vp);
int v = (int)((mark*100.0) / progress_report_count);
int percent = v < 100 ? v : 99;
- if (percent > pw->value()) pw->setValue(percent);
- QApplication::processEvents();
- if (pw->wasCanceled()) throw ProgressCancelException();
-#else
- QProgressDialog *pd = static_cast<QProgressDialog*>(vp);
- int v = (int)((mark*100.0) / progress_report_count);
- pd->setValue(v < 100 ? v : 99);
- QString label;
- label.sprintf("Rendering Polygon Mesh (%d/%d)", mark, progress_report_count);
- pd->setLabelText(label);
- QApplication::processEvents();
- if (pd->wasCanceled()) throw ProgressCancelException();
-#endif
+
+ if (percent > thisp->progresswidget->value()) {
+ QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection,
+ Q_ARG(int, percent));
+ }
+
+ if (thisp->progresswidget->wasCanceled()) throw ProgressCancelException();
}
/*!
@@ -740,21 +733,11 @@ void MainWindow::compileCSG(bool procevents)
QTime t;
t.start();
-#ifdef USE_PROGRESSWIDGET
- ProgressWidget *pd = new ProgressWidget(this);
- pd->setRange(0, 100);
- pd->setValue(0);
- connect(pd, SIGNAL(requestShow()), this, SLOT(showProgress()));
-#else
- QProgressDialog *pd = new QProgressDialog("Rendering CSG products...", "Cancel", 0, 100);
- pd->setRange(0, 100);
- pd->setValue(0);
- pd->setAutoClose(false);
- pd->show();
-#endif
+ this->progresswidget = new ProgressWidget(this);
+ connect(this->progresswidget, SIGNAL(requestShow()), this, SLOT(showProgress()));
QApplication::processEvents();
- progress_report_prep(root_node, report_func, pd);
+ progress_report_prep(root_node, report_func, this);
try {
CGALEvaluator cgalevaluator(this->tree);
PolySetCGALEvaluator psevaluator(cgalevaluator);
@@ -772,10 +755,9 @@ void MainWindow::compileCSG(bool procevents)
PRINT("CSG generation cancelled.");
}
progress_report_fin();
-#ifdef USE_PROGRESSWIDGET
- this->statusBar()->removeWidget(pd);
-#endif
- delete pd;
+ this->statusBar()->removeWidget(this->progresswidget);
+ delete this->progresswidget;
+ this->progresswidget = NULL;
if (root_raw_term) {
PRINT("Compiling design (CSG Products normalization)...");
@@ -1150,7 +1132,7 @@ void MainWindow::actionCompile()
void MainWindow::actionRenderCGAL()
{
if (GuiLocker::isLocked()) return;
- GuiLocker lock;
+ GuiLocker::lock();
setCurrentOutput();
console->clear();
@@ -1172,88 +1154,52 @@ void MainWindow::actionRenderCGAL()
PRINT("Rendering Polygon Mesh using CGAL...");
QApplication::processEvents();
- QTime t;
- t.start();
+ this->progresswidget = new ProgressWidget(this);
+ connect(this->progresswidget, SIGNAL(requestShow()), this, SLOT(showProgress()));
+ QApplication::processEvents();
-#ifdef USE_PROGRESSWIDGET
- ProgressWidget *pd = new ProgressWidget(this);
- pd->setRange(0, 100);
- pd->setValue(0);
- connect(pd, SIGNAL(requestShow()), this, SLOT(showProgress()));
-#else
- QProgressDialog *pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", "Cancel", 0, 100);
- pd->setRange(0, 100);
- pd->setValue(0);
- pd->setAutoClose(false);
- pd->show();
-#endif
+ progress_report_prep(this->root_node, report_func, this);
- QApplication::processEvents();
+ this->cgalworker->start(this->tree);
+}
- progress_report_prep(this->root_node, report_func, pd);
- try {
- CGALEvaluator evaluator(this->tree);
- this->root_N = new CGAL_Nef_polyhedron(evaluator.evaluateCGALMesh(*this->root_node));
- PolySetCache::instance()->print();
- CGALCache::instance()->print();
- }
- catch (ProgressCancelException e) {
- PRINT("Rendering cancelled.");
- }
+void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N)
+{
progress_report_fin();
- if (this->root_N)
- {
- // FIXME: Reenable cache cost info
-// PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost());
-// PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size());
- QApplication::processEvents();
+ if (root_N) {
+ PolySetCache::instance()->print();
+ CGALCache::instance()->print();
- if (this->root_N->dim == 2) {
+ if (root_N->dim == 2) {
PRINTF(" Top level object is a 2D object:");
- QApplication::processEvents();
- PRINTF(" Empty: %6s", this->root_N->p2->is_empty() ? "yes" : "no");
- QApplication::processEvents();
- PRINTF(" Plane: %6s", this->root_N->p2->is_plane() ? "yes" : "no");
- QApplication::processEvents();
- PRINTF(" Vertices: %6d", (int)this->root_N->p2->explorer().number_of_vertices());
- QApplication::processEvents();
- PRINTF(" Halfedges: %6d", (int)this->root_N->p2->explorer().number_of_halfedges());
- QApplication::processEvents();
- PRINTF(" Edges: %6d", (int)this->root_N->p2->explorer().number_of_edges());
- QApplication::processEvents();
- PRINTF(" Faces: %6d", (int)this->root_N->p2->explorer().number_of_faces());
- QApplication::processEvents();
- PRINTF(" FaceCycles: %6d", (int)this->root_N->p2->explorer().number_of_face_cycles());
- QApplication::processEvents();
- PRINTF(" ConnComp: %6d", (int)this->root_N->p2->explorer().number_of_connected_components());
- QApplication::processEvents();
+ PRINTF(" Empty: %6s", root_N->p2->is_empty() ? "yes" : "no");
+ PRINTF(" Plane: %6s", root_N->p2->is_plane() ? "yes" : "no");
+ PRINTF(" Vertices: %6d", (int)root_N->p2->explorer().number_of_vertices());
+ PRINTF(" Halfedges: %6d", (int)root_N->p2->explorer().number_of_halfedges());
+ PRINTF(" Edges: %6d", (int)root_N->p2->explorer().number_of_edges());
+ PRINTF(" Faces: %6d", (int)root_N->p2->explorer().number_of_faces());
+ PRINTF(" FaceCycles: %6d", (int)root_N->p2->explorer().number_of_face_cycles());
+ PRINTF(" ConnComp: %6d", (int)root_N->p2->explorer().number_of_connected_components());
}
- if (this->root_N->dim == 3) {
+ if (root_N->dim == 3) {
PRINTF(" Top level object is a 3D object:");
- PRINTF(" Simple: %6s", this->root_N->p3->is_simple() ? "yes" : "no");
- QApplication::processEvents();
- PRINTF(" Valid: %6s", this->root_N->p3->is_valid() ? "yes" : "no");
- QApplication::processEvents();
- PRINTF(" Vertices: %6d", (int)this->root_N->p3->number_of_vertices());
- QApplication::processEvents();
- PRINTF(" Halfedges: %6d", (int)this->root_N->p3->number_of_halfedges());
- QApplication::processEvents();
- PRINTF(" Edges: %6d", (int)this->root_N->p3->number_of_edges());
- QApplication::processEvents();
- PRINTF(" Halffacets: %6d", (int)this->root_N->p3->number_of_halffacets());
- QApplication::processEvents();
- PRINTF(" Facets: %6d", (int)this->root_N->p3->number_of_facets());
- QApplication::processEvents();
- PRINTF(" Volumes: %6d", (int)this->root_N->p3->number_of_volumes());
- QApplication::processEvents();
+ PRINTF(" Simple: %6s", root_N->p3->is_simple() ? "yes" : "no");
+ PRINTF(" Valid: %6s", root_N->p3->is_valid() ? "yes" : "no");
+ PRINTF(" Vertices: %6d", (int)root_N->p3->number_of_vertices());
+ PRINTF(" Halfedges: %6d", (int)root_N->p3->number_of_halfedges());
+ PRINTF(" Edges: %6d", (int)root_N->p3->number_of_edges());
+ PRINTF(" Halffacets: %6d", (int)root_N->p3->number_of_halffacets());
+ PRINTF(" Facets: %6d", (int)root_N->p3->number_of_facets());
+ PRINTF(" Volumes: %6d", (int)root_N->p3->number_of_volumes());
}
- int s = t.elapsed() / 1000;
+ int s = this->progresswidget->elapsedTime() / 1000;
PRINTF("Total rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60);
+ this->root_N = root_N;
if (!this->root_N->empty()) {
this->cgalRenderer = new CGALRenderer(*this->root_N);
// Go to CGAL view mode
@@ -1271,11 +1217,12 @@ void MainWindow::actionRenderCGAL()
}
}
-#ifdef USE_PROGRESSWIDGET
- this->statusBar()->removeWidget(pd);
-#endif
- delete pd;
+ this->statusBar()->removeWidget(this->progresswidget);
+ delete this->progresswidget;
+ this->progresswidget = NULL;
clearCurrentOutput();
+
+ GuiLocker::unlock();
}
#endif /* ENABLE_CGAL */
@@ -1827,6 +1774,15 @@ void MainWindow::quit()
if (ev.isAccepted()) QApplication::instance()->quit();
}
+void MainWindow::consoleOutput(const std::string &msg, void *userdata)
+{
+ // Invoke the append function in the main thread in case the output
+ // originates in a worker thread.
+ MainWindow *thisp = static_cast<MainWindow*>(userdata);
+ QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection,
+ Q_ARG(QString, QString::fromStdString(msg)));
+}
+
void MainWindow::setCurrentOutput()
{
set_output_handler(&MainWindow::consoleOutput, this);
@@ -1836,4 +1792,3 @@ void MainWindow::clearCurrentOutput()
{
set_output_handler(NULL, NULL);
}
-
contact: Jan Huwald // Impressum