From b540689a832244cfc6f4c5e62f2b01644f5bc437 Mon Sep 17 00:00:00 2001 From: "Simon A. Eugster" Date: Tue, 20 Jul 2010 16:36:31 +0000 Subject: [PATCH] AbstractScopeWidget changes: * Support for realtime updates * Semaphores explained, comments added svn path=/trunk/kdenlive/; revision=4609 --- src/abstractscopewidget.cpp | 65 +++++++++++++++++------ src/abstractscopewidget.h | 58 ++++++++++++++------ src/colorcorrection/waveformgenerator.cpp | 10 ++-- src/colorcorrection/waveformgenerator.h | 2 +- src/testwidget.cpp | 12 ++--- src/testwidget.h | 6 +-- src/waveform.cpp | 14 ++--- src/waveform.h | 6 +-- 8 files changed, 117 insertions(+), 56 deletions(-) diff --git a/src/abstractscopewidget.cpp b/src/abstractscopewidget.cpp index de7d4c93..62958982 100644 --- a/src/abstractscopewidget.cpp +++ b/src/abstractscopewidget.cpp @@ -20,6 +20,8 @@ #include #include +const int REALTIME_FPS = 30; + const QColor light(250, 238, 226, 255); const QColor dark ( 40, 40, 39, 255); const QColor dark2( 25, 25, 23, 255); @@ -32,6 +34,9 @@ AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMoni m_semaphoreHUD(1), m_semaphoreScope(1), m_semaphoreBackground(1), + m_accelFactorHUD(1), + m_accelFactorScope(1), + m_accelFactorBackground(1), initialDimensionUpdateDone(false) { @@ -49,7 +54,6 @@ AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMoni m_aAutoRefresh->setCheckable(true); m_aRealtime = new QAction(i18n("Realtime (with precision loss)"), this); m_aRealtime->setCheckable(true); - m_aRealtime->setEnabled(false); m_menu = new QMenu(this); m_menu->setPalette(m_scopePalette); @@ -64,14 +68,17 @@ AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMoni m_activeRender = m_clipMonitor->render; } - connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); + bool b = true; - connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); - connect(this, SIGNAL(signalHUDRenderingFinished(uint)), this, SLOT(slotHUDRenderingFinished(uint))); - connect(this, SIGNAL(signalScopeRenderingFinished(uint)), this, SLOT(slotScopeRenderingFinished(uint))); - connect(this, SIGNAL(signalBackgroundRenderingFinished(uint)), this, SLOT(slotBackgroundRenderingFinished(uint))); + b &= connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); + b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); + b &= connect(this, SIGNAL(signalHUDRenderingFinished(uint,uint)), this, SLOT(slotHUDRenderingFinished(uint,uint))); + b &= connect(this, SIGNAL(signalScopeRenderingFinished(uint,uint)), this, SLOT(slotScopeRenderingFinished(uint,uint))); + b &= connect(this, SIGNAL(signalBackgroundRenderingFinished(uint,uint)), this, SLOT(slotBackgroundRenderingFinished(uint,uint))); + b &= connect(m_aRealtime, SIGNAL(toggled(bool)), this, SLOT(slotResetRealtimeFactor(bool))); + Q_ASSERT(b); } AbstractScopeWidget::~AbstractScopeWidget() @@ -84,12 +91,15 @@ void AbstractScopeWidget::prodHUDThread() {} void AbstractScopeWidget::prodScopeThread() { + // Try to acquire the semaphore. This must only succeed if m_threadScope is not running + // anymore. Therefore the semaphore must NOT be released before m_threadScope ends. + // If acquiring the semaphore fails, the thread is still running. if (m_semaphoreScope.tryAcquire(1)) { Q_ASSERT(!m_threadScope.isRunning()); m_newScopeFrames.fetchAndStoreRelaxed(0); m_newScopeUpdates.fetchAndStoreRelaxed(0); - m_threadScope = QtConcurrent::run(this, &AbstractScopeWidget::renderScope); + m_threadScope = QtConcurrent::run(this, &AbstractScopeWidget::renderScope, m_accelFactorScope); qDebug() << "Scope thread started in " << widgetName(); } else { @@ -153,29 +163,43 @@ void AbstractScopeWidget::customContextMenuRequested(const QPoint &pos) ///// Slots ///// -void AbstractScopeWidget::slotHUDRenderingFinished(uint) -{ - -} +void AbstractScopeWidget::slotHUDRenderingFinished(uint, uint) {} -void AbstractScopeWidget::slotScopeRenderingFinished(uint) +void AbstractScopeWidget::slotScopeRenderingFinished(uint mseconds, uint) { + // The signal can be received before the thread has really finished. So we + // need to wait until it has really finished before starting a new thread. qDebug() << "Scope rendering has finished, waiting for termination in " << widgetName(); m_threadScope.waitForFinished(); m_imgScope = m_threadScope.result(); + + // The scope thread has finished. Now we can release the semaphore, allowing a new thread. + // See prodScopeThread where the semaphore is acquired again. m_semaphoreScope.release(1); this->update(); + // Calculate the acceleration factor hint to get «realtime» updates. + int accel; + if (m_aRealtime->isChecked()) { + accel = ceil((float)mseconds*REALTIME_FPS/1000 ); + if (m_accelFactorScope < 1) { + // If mseconds is 0. + accel = 1; + } + // Don't directly calculate with m_accelFactorScope as we are dealing with concurrency. + // If m_accelFactorScope is set to 0 at the wrong moment, who knows what might happen + // then :) Therefore use a local variable. + m_accelFactorScope = accel; + } + if ( (m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) { qDebug() << "Trying to start a new thread for " << widgetName() << ". New frames/updates: " << m_newScopeFrames << "/" << m_newScopeUpdates; + prodScopeThread(); } } -void AbstractScopeWidget::slotBackgroundRenderingFinished(uint) -{ - -} +void AbstractScopeWidget::slotBackgroundRenderingFinished(uint, uint) {} void AbstractScopeWidget::slotActiveMonitorChanged(bool isClipMonitor) { @@ -214,3 +238,12 @@ void AbstractScopeWidget::slotRenderZoneUpdated() } } } + +void AbstractScopeWidget::slotResetRealtimeFactor(bool realtimeChecked) +{ + if (!realtimeChecked) { + m_accelFactorHUD = 1; + m_accelFactorScope = 1; + m_accelFactorBackground = 1; + } +} diff --git a/src/abstractscopewidget.h b/src/abstractscopewidget.h index a7684bfd..67c76c9e 100644 --- a/src/abstractscopewidget.h +++ b/src/abstractscopewidget.h @@ -77,15 +77,28 @@ protected: Monitor *m_clipMonitor; Render *m_activeRender; + /** The context menu. Feel free to add new entries in your implementation. */ QMenu *m_menu; + + /** Enables auto refreshing of the scope. + This is when a new frame is shown on the active monitor. + Resize events always force a recalculation. */ QAction *m_aAutoRefresh; + + /** Realtime rendering. Should be disabled if it is not supported. + Use the accelerationFactor variable passed to the render functions as a hint of + how many times faster the scope should be calculated. */ QAction *m_aRealtime; + /** Offset from the widget's borders */ const uchar offset; + /** The rect on the widget we're painting in. */ QRect m_scopeRect; + + /** Images storing the calculated layers. Will be used on repaint events. */ QImage m_imgHUD; QImage m_imgScope; QImage m_imgBackground; @@ -96,18 +109,18 @@ protected: /** Where on the widget we can paint in */ virtual QRect scopeRect() = 0; - /** @brief HUD renderer. - Must emit signalHUDRenderingFinished(). - Should */ - virtual QImage renderHUD() = 0; - /** @brief Scope renderer. Must emit signalScopeRenderingFinished(). */ - virtual QImage renderScope() = 0; - /** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). */ - virtual QImage renderBackground() = 0; + /** @brief HUD renderer. Must emit signalHUDRenderingFinished(). @see renderScope */ + virtual QImage renderHUD(uint accelerationFactor) = 0; + /** @brief Scope renderer. Must emit signalScopeRenderingFinished() + when calculation has finished, to allow multi-threading. + accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. */ + virtual QImage renderScope(uint accelerationFactor) = 0; + /** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). @see renderScope */ + virtual QImage renderBackground(uint accelerationFactor) = 0; /** Must return true if the HUD layer depends on the input monitor. - If it does not, then it does not need to be re-calculated when - a new frame from the monitor is incoming. */ + If it does not, then it does not need to be re-calculated when + a new frame from the monitor is incoming. */ virtual bool isHUDDependingOnInput() const = 0; /** @see isHUDDependingOnInput() */ virtual bool isScopeDependingOnInput() const = 0; @@ -121,9 +134,11 @@ protected: void resizeEvent(QResizeEvent *); signals: - void signalHUDRenderingFinished(uint mseconds); - void signalScopeRenderingFinished(uint mseconds); - void signalBackgroundRenderingFinished(uint mseconds); + /** mseconds represent the time taken for the calculation, + accelerationFactor the acceleration factor that has been used. */ + void signalHUDRenderingFinished(uint mseconds, uint accelerationFactor); + void signalScopeRenderingFinished(uint mseconds, uint accelerationFactor); + void signalBackgroundRenderingFinished(uint mseconds, uint accelerationFactor); private: @@ -139,6 +154,9 @@ private: QAtomicInt m_newScopeUpdates; QAtomicInt m_newBackgroundUpdates; + /** The semaphores ensure that the QFutures for the HUD/Scope/Background threads cannot + be assigned a new thread while it is still running. (Could cause deadlocks and other + nasty things known from parallelism. */ QSemaphore m_semaphoreHUD; QSemaphore m_semaphoreScope; QSemaphore m_semaphoreBackground; @@ -147,11 +165,16 @@ private: QFuture m_threadScope; QFuture m_threadBackground; + int m_accelFactorHUD; + int m_accelFactorScope; + int m_accelFactorBackground; + bool initialDimensionUpdateDone; void prodHUDThread(); void prodScopeThread(); void prodBackgroundThread(); + private slots: /** @brief Must be called when the active monitor has shown a new frame. This slot must be connected in the implementing class, it is *not* @@ -159,9 +182,12 @@ private slots: void slotActiveMonitorChanged(bool isClipMonitor); void customContextMenuRequested(const QPoint &pos); void slotRenderZoneUpdated(); - void slotHUDRenderingFinished(uint mseconds); - void slotScopeRenderingFinished(uint mseconds); - void slotBackgroundRenderingFinished(uint mseconds); + void slotHUDRenderingFinished(uint mseconds, uint accelerationFactor); + void slotScopeRenderingFinished(uint mseconds, uint accelerationFactor); + void slotBackgroundRenderingFinished(uint mseconds, uint accelerationFactor); + + /** Resets the acceleration factors to 1 when realtime rendering is disabled. */ + void slotResetRealtimeFactor(bool realtimeChecked); }; diff --git a/src/colorcorrection/waveformgenerator.cpp b/src/colorcorrection/waveformgenerator.cpp index 195da5ac..0575a1c5 100644 --- a/src/colorcorrection/waveformgenerator.cpp +++ b/src/colorcorrection/waveformgenerator.cpp @@ -25,8 +25,10 @@ WaveformGenerator::~WaveformGenerator() { } -QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QImage &image, const bool &drawAxis) +QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QImage &image, const bool &drawAxis, const uint &accelFactor) { + Q_ASSERT(accelFactor >= 1); + QTime time; time.start(); @@ -60,7 +62,7 @@ QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QIm const float wPrediv = (float)(ww-1)/(iw-1); const uchar *bits = image.bits(); - const uint stepsize = 4; + const uint stepsize = 4*accelFactor; for (uint i = 0, x = 0; i < byteCount; i += stepsize) { @@ -80,7 +82,7 @@ QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QIm bits += stepsize; x += stepsize; - x %= iw; + x %= iw; // Modulo image width, to represent the current x position in the image } if (drawAxis) { @@ -102,7 +104,7 @@ QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QIm } uint diff = time.elapsed(); - qDebug() << "Waveform calculation ended. Time taken: " << diff << " ms. Sending signal now."; +// qDebug() << "Waveform calculation ended. Time taken: " << diff << " ms. Sending signal now."; emit signalCalculationFinished(wave, diff); return wave; diff --git a/src/colorcorrection/waveformgenerator.h b/src/colorcorrection/waveformgenerator.h index 10e86a94..d3abd604 100644 --- a/src/colorcorrection/waveformgenerator.h +++ b/src/colorcorrection/waveformgenerator.h @@ -23,7 +23,7 @@ public: WaveformGenerator(); ~WaveformGenerator(); - QImage calculateWaveform(const QSize &waveformSize, const QImage &image, const bool &drawAxis); + QImage calculateWaveform(const QSize &waveformSize, const QImage &image, const bool &drawAxis, const uint &accelFactor = 1); signals: void signalCalculationFinished(QImage image, const uint &ms); diff --git a/src/testwidget.cpp b/src/testwidget.cpp index 2cf5eedb..0e6c942a 100644 --- a/src/testwidget.cpp +++ b/src/testwidget.cpp @@ -32,21 +32,21 @@ TestWidget::~TestWidget() ///// Implemented Methods ///// -QImage TestWidget::renderHUD() +QImage TestWidget::renderHUD(uint) { - emit signalHUDRenderingFinished(0); + emit signalHUDRenderingFinished(0, 1); return QImage(); } -QImage TestWidget::renderScope() +QImage TestWidget::renderScope(uint) { - emit signalScopeRenderingFinished(0); + emit signalScopeRenderingFinished(0, 1); return QImage(); } -QImage TestWidget::renderBackground() +QImage TestWidget::renderBackground(uint) { - emit signalBackgroundRenderingFinished(0); + emit signalBackgroundRenderingFinished(0, 1); return QImage(); } diff --git a/src/testwidget.h b/src/testwidget.h index fa3c0ac3..ff567572 100644 --- a/src/testwidget.h +++ b/src/testwidget.h @@ -27,9 +27,9 @@ public: QString widgetName() const; /// Implemented methods /// - QImage renderHUD(); - QImage renderScope(); - QImage renderBackground(); + QImage renderHUD(uint accelerationFactor); + QImage renderScope(uint accelerationFactor); + QImage renderBackground(uint accelerationFactor); bool isHUDDependingOnInput() const; bool isScopeDependingOnInput() const; bool isBackgroundDependingOnInput() const; diff --git a/src/waveform.cpp b/src/waveform.cpp index d7248e30..911ecd51 100644 --- a/src/waveform.cpp +++ b/src/waveform.cpp @@ -52,26 +52,26 @@ bool Waveform::isHUDDependingOnInput() const { return false; } bool Waveform::isScopeDependingOnInput() const { return true; } bool Waveform::isBackgroundDependingOnInput() const { return false; } -QImage Waveform::renderHUD() +QImage Waveform::renderHUD(uint) { - emit signalHUDRenderingFinished(0); + emit signalHUDRenderingFinished(0, 1); return QImage(); } -QImage Waveform::renderScope() +QImage Waveform::renderScope(uint accelFactor) { QTime start = QTime::currentTime(); start.start(); QImage wave = m_waveformGenerator->calculateWaveform(scopeRect().size(), - m_activeRender->extractFrame(m_activeRender->seekFramePosition()), true); + m_activeRender->extractFrame(m_activeRender->seekFramePosition()), true, accelFactor); - emit signalScopeRenderingFinished(start.elapsed()); + emit signalScopeRenderingFinished(start.elapsed(), 1); return wave; } -QImage Waveform::renderBackground() +QImage Waveform::renderBackground(uint) { - emit signalBackgroundRenderingFinished(0); + emit signalBackgroundRenderingFinished(0, 1); return QImage(); } diff --git a/src/waveform.h b/src/waveform.h index b51bcf6e..7e4f0739 100644 --- a/src/waveform.h +++ b/src/waveform.h @@ -38,9 +38,9 @@ private: /// Implemented methods /// QRect scopeRect(); - QImage renderHUD(); - QImage renderScope(); - QImage renderBackground(); + QImage renderHUD(uint); + QImage renderScope(uint); + QImage renderBackground(uint); bool isHUDDependingOnInput() const; bool isScopeDependingOnInput() const; bool isBackgroundDependingOnInput() const; -- 2.39.2