mirror of https://github.com/vitalif/openscad
Perform CGAL evaluation in a separate thread. First steps towards better GUI responsiveness and parallelization
parent
3e64e63b01
commit
7c48b345b1
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
183
src/mainwin.cc
183
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();
|
||||
|
||||
|
||||
#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
|
||||
this->progresswidget = new ProgressWidget(this);
|
||||
connect(this->progresswidget, SIGNAL(requestShow()), this, SLOT(showProgress()));
|
||||
|
||||
QApplication::processEvents();
|
||||
|
||||
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.");
|
||||
}
|
||||
progress_report_prep(this->root_node, report_func, this);
|
||||
|
||||
this->cgalworker->start(this->tree);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue