diff --git a/src/libcore/lock.cpp b/src/libcore/lock.cpp index 80dd2bb5..eeb8a568 100644 --- a/src/libcore/lock.cpp +++ b/src/libcore/lock.cpp @@ -74,12 +74,15 @@ bool ConditionVariable::wait(int ms) { struct timespec ts; ts.tv_sec = tv.tv_sec + ms/1000; - ts.tv_nsec = (tv.tv_usec + ms % 1000) * 1000; - + ts.tv_nsec = tv.tv_usec * 1000 + (ms % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } int retval = pthread_cond_timedwait(&m_cond, &m_mutex->m_mutex, &ts); #if defined(WIN32) /* Should be outside of the switch statement (depending - on the used compiler, the constants ETIMEDOUT and WSAETIMEDOUT + on the used environment, the constants ETIMEDOUT and WSAETIMEDOUT are potentially identical */ if (retval == WSAETIMEDOUT) return false; diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp index 05a1a669..698cb91c 100644 --- a/src/mtsgui/mainwindow.cpp +++ b/src/mtsgui/mainwindow.cpp @@ -163,12 +163,11 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_renderListener, SIGNAL(jobFinished(const RenderJob *, bool)), this, SLOT(onJobFinished(const RenderJob *, bool)), Qt::QueuedConnection); - connect(m_renderListener, SIGNAL(refresh(const RenderJob *, const Bitmap *)), - this, SLOT(onRefresh(const RenderJob *, const Bitmap *)), Qt::BlockingQueuedConnection); connect(m_renderListener, SIGNAL(workEnd(const RenderJob *, const ImageBlock *)), this, SLOT(onWorkEnd(const RenderJob *, const ImageBlock *)), Qt::DirectConnection); connect(m_renderListener, SIGNAL(workBegin(const RenderJob *, const RectangularWorkUnit *, int)), this, SLOT(onWorkBegin(const RenderJob *, const RectangularWorkUnit *, int)), Qt::DirectConnection); + connect(m_renderListener, SIGNAL(refresh()), this, SLOT(onRefresh()), Qt::QueuedConnection); connect(m_consoleAppender, SIGNAL(progressMessage(const RenderJob *, const QString &, float, const QString &)), this, SLOT(onProgressMessage(const RenderJob *, const QString &, float, const QString &)), @@ -1513,7 +1512,7 @@ void MainWindow::onJobFinished(const RenderJob *job, bool cancelled) { ui->glView->resumePreview(); } } - onRefresh(job, NULL); + refresh(job, NULL); context->renderJob = NULL; updateUI(); if (ui->tabBar->currentIndex() != -1 && @@ -1673,10 +1672,18 @@ void MainWindow::onWorkEnd(const RenderJob *job, const ImageBlock *block) { emit updateView(); } -void MainWindow::onRefresh(const RenderJob *job, const Bitmap *_bitmap) { +void MainWindow::onRefresh() { + const QRenderListener::RefreshRequest *req = m_renderListener->acquireRefreshRequest(); + if (req) + refresh(req->first, req->second); + m_renderListener->releaseRefreshRequest(); +} + +void MainWindow::refresh(const RenderJob *job, const Bitmap *_bitmap) { SceneContext *context = getContext(job, false); if (context == NULL) return; + Film *film = context->scene->getFilm(); Point2i co = film->getCropOffset(); Bitmap *bitmap = context->framebuffer; diff --git a/src/mtsgui/mainwindow.h b/src/mtsgui/mainwindow.h index bfd1c7a0..cc32f703 100644 --- a/src/mtsgui/mainwindow.h +++ b/src/mtsgui/mainwindow.h @@ -25,6 +25,10 @@ #define MAX_RECENT_FILES 10 +// amount of time that a rendering thread will wait for the GUI to accept a +// render view refresh request to show intermediate progress (in ms) +#define REFRESH_TIMEOUT 50 + namespace Ui { class MainWindow; } @@ -41,6 +45,14 @@ class PreviewSettingsDlg; class QRenderListener : public QObject, public RenderListener { Q_OBJECT public: + typedef std::pair RefreshRequest; + + QRenderListener() { + m_mutex = new Mutex(); + m_cond = new ConditionVariable(m_mutex); + m_refreshRequest = NULL; + } + /// Called when work has begun in a rectangular image region inline void workBeginEvent(const RenderJob *job, const RectangularWorkUnit *wu, int worker) { emit workBegin(job, wu, worker); @@ -50,10 +62,25 @@ public: inline void workEndEvent(const RenderJob *job, const ImageBlock *wr) { emit workEnd(job, wr); } + /// Called when the whole target image has been altered in some way inline void refreshEvent(const RenderJob *job, const Bitmap *bitmap) { - emit refresh(job, bitmap); + m_mutex->lock(); + RefreshRequest request = std::make_pair(job, bitmap); + + /* Potentially overwrite a previous refresh request */ + m_refreshRequest = &request; + + /* Asynchronously signal the GUI */ + emit refresh(); + + /* Wait for the GUI to draw the image (or another + thread to overwrite the refresh request) */ + m_cond->wait(REFRESH_TIMEOUT); + + m_refreshRequest = NULL; + m_mutex->unlock(); } /// Called when a render job has completed successfully or unsuccessfully @@ -61,14 +88,23 @@ public: emit jobFinished(job, cancelled); } + inline const RefreshRequest *acquireRefreshRequest() { m_mutex->lock(); return m_refreshRequest; } + inline void releaseRefreshRequest() { m_refreshRequest = NULL; m_cond->signal(); m_mutex->unlock(); } + MTS_DECLARE_CLASS() signals: void workBegin(const RenderJob *job, const RectangularWorkUnit *wu, int worker); void workEnd(const RenderJob *job, const ImageBlock *wr); - void refresh(const RenderJob *job, const Bitmap *bitmap); void jobFinished(const RenderJob *job, bool cancelled); + void refresh(); + protected: virtual ~QRenderListener() { } + +private: + ref m_mutex; + ref m_cond; + RefreshRequest *m_refreshRequest; }; class PreviewSettingsDialog; @@ -97,6 +133,7 @@ protected: void drawVisualWorkUnit(SceneContext *context, const VisualWorkUnit &block); void checkForUpdates(bool notifyIfNone = false); void saveAs(SceneContext *ctx, const QString &targetFile); + void refresh(const RenderJob *job, const Bitmap *bitmap); QSize sizeHint() const; signals: @@ -137,7 +174,7 @@ private slots: void onClearRecent(); void onWorkBegin(const RenderJob *job, const RectangularWorkUnit *wu, int worker); void onWorkEnd(const RenderJob *job, const ImageBlock *wr); - void onRefresh(const RenderJob *job, const Bitmap *bitmap); + void onRefresh(); void onJobFinished(const RenderJob *job, bool cancelled); void onProgressMessage(const RenderJob *job, const QString &name, float progress, const QString &eta);