]> git.sesse.net Git - kdenlive/commitdiff
AbstractScopeWidget changes:
authorSimon A. Eugster <simon.eu@gmail.com>
Tue, 20 Jul 2010 16:36:31 +0000 (16:36 +0000)
committerSimon A. Eugster <simon.eu@gmail.com>
Tue, 20 Jul 2010 16:36:31 +0000 (16:36 +0000)
* Support for realtime updates
* Semaphores explained, comments added

svn path=/trunk/kdenlive/; revision=4609

src/abstractscopewidget.cpp
src/abstractscopewidget.h
src/colorcorrection/waveformgenerator.cpp
src/colorcorrection/waveformgenerator.h
src/testwidget.cpp
src/testwidget.h
src/waveform.cpp
src/waveform.h

index de7d4c93c6ee7b5976c890a4894e13c80d157b2b..62958982ca824866189eb98b49f67ee269485b97 100644 (file)
@@ -20,6 +20,8 @@
 #include <QMenu>
 #include <QPainter>
 
+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;
+    }
+}
index a7684bfde816f49b2574d8c0c304ba547835cfe8..67c76c9e4be27cddb7dc47aa71b3b7c4972e886c 100644 (file)
@@ -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<QImage> m_threadScope;
     QFuture<QImage> 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);
 
 };
 
index 195da5ac19b00da7d1ca8c7d463cb1678b04b5d9..0575a1c500a66f1bf7bc1b3982535676c6f79058 100644 (file)
@@ -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;
index 10e86a9431ce4c503e9796ce87b3bf88b7f58036..d3abd6047b2b8b673fc07381358780d5f7a87cd1 100644 (file)
@@ -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);
index 2cf5eedbd1ee9ea7c7b07d63526ca07bb03d91fd..0e6c942aca4a14538c92b154b8720747e7f364fc 100644 (file)
@@ -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();
 }
 
index fa3c0ac3ddff6eeca7cd982f2e3cb5319d786e59..ff5675722844241ac75fefa260c517af8b7dd85a 100644 (file)
@@ -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;
index d7248e3053ae10de96fc72e2655b5406e3e3db04..911ecd510019686d723558ed035873bfc8340e3e 100644 (file)
@@ -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();
 }
index b51bcf6ed71145a399d9759da9616054155ffef5..7e4f07392fa6d06095f26a102b4b8b6fee0141c4 100644 (file)
@@ -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;