From f08ed0ff7d621f207e1c3f3c81c98debb7c23248 Mon Sep 17 00:00:00 2001 From: Vort Date: Mon, 10 Aug 2020 15:54:34 +0300 Subject: [PATCH] ATV Demodulator: implement double buffering --- plugins/channelrx/demodatv/atvdemodsink.cpp | 4 +- plugins/channelrx/demodatv/atvdemodsink.h | 12 +++--- sdrgui/gui/tvscreenanalog.cpp | 46 +++++++++++++-------- sdrgui/gui/tvscreenanalog.h | 19 +++++---- 4 files changed, 46 insertions(+), 35 deletions(-) diff --git a/plugins/channelrx/demodatv/atvdemodsink.cpp b/plugins/channelrx/demodatv/atvdemodsink.cpp index 72b9c7a2f..09f1559e1 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.cpp +++ b/plugins/channelrx/demodatv/atvdemodsink.cpp @@ -484,7 +484,7 @@ void ATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque m_samplesPerLine - m_numberSamplesPerLineSignals, m_settings.m_nbLines - m_numberOfBlackLines ); - m_tvScreenData = m_registeredTVScreen->getData(); + m_tvScreenBuffer = m_registeredTVScreen->getBackBuffer(); } m_fieldIndex = 0; @@ -576,7 +576,7 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force) m_samplesPerLine - m_numberSamplesPerLineSignals, m_settings.m_nbLines - m_numberOfBlackLines ); - m_tvScreenData = m_registeredTVScreen->getData(); + m_tvScreenBuffer = m_registeredTVScreen->getBackBuffer(); } m_fieldIndex = 0; diff --git a/plugins/channelrx/demodatv/atvdemodsink.h b/plugins/channelrx/demodatv/atvdemodsink.h index 40e48eb02..53221a91e 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.h +++ b/plugins/channelrx/demodatv/atvdemodsink.h @@ -109,7 +109,7 @@ private: //*************** ATV PARAMETERS *************** TVScreenAnalog *m_registeredTVScreen; - std::shared_ptr m_tvScreenData; + std::shared_ptr m_tvScreenBuffer; //int m_intNumberSamplePerLine; int m_numberSamplesPerHTopNom; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - nominal value @@ -199,7 +199,7 @@ private: inline void processSample(float& sample, int& sampleVideo) { // Filling pixel on the current line - reference index 0 at start of sync pulse - m_tvScreenData->setSampleValue(m_sampleOffset - m_numberSamplesPerHSync, sampleVideo); + m_tvScreenBuffer->setSampleValue(m_sampleOffset - m_numberSamplesPerHSync, sampleVideo); if (m_settings.m_hSync) { @@ -278,7 +278,7 @@ private: { if (m_lineIndex == m_numberOfVSyncLines + 3 && m_fieldIndex == 0) { - m_registeredTVScreen->renderImage(); + m_tvScreenBuffer = m_registeredTVScreen->swapBuffers(); } if (m_vSyncDetectSampleCount > m_vSyncDetectThreshold && @@ -311,7 +311,7 @@ private: if (m_interleaved) rowIndex = rowIndex * 2 - m_fieldIndex; - m_tvScreenData->selectRow(rowIndex, m_sampleOffsetFrac); + m_tvScreenBuffer->selectRow(rowIndex, m_sampleOffsetFrac); } // Vertical sync is obtained by skipping horizontal sync on the line that triggers vertical sync (new frame) @@ -320,11 +320,11 @@ private: if ((m_sampleOffsetDetected > (3 * m_samplesPerLine) / 2) // Vertical sync is first horizontal sync after skip (count at least 1.5 line length) || (!m_settings.m_vSync && (m_lineIndex >= m_settings.m_nbLines))) // Vsync ignored and reached nominal number of lines per frame { - m_registeredTVScreen->renderImage(); + m_tvScreenBuffer = m_registeredTVScreen->swapBuffers(); m_lineIndex = 0; } - m_tvScreenData->selectRow(m_lineIndex, m_sampleOffsetFrac); + m_tvScreenBuffer->selectRow(m_lineIndex, m_sampleOffsetFrac); } }; diff --git a/sdrgui/gui/tvscreenanalog.cpp b/sdrgui/gui/tvscreenanalog.cpp index 5b6766515..48413fd84 100644 --- a/sdrgui/gui/tvscreenanalog.cpp +++ b/sdrgui/gui/tvscreenanalog.cpp @@ -68,10 +68,11 @@ TVScreenAnalog::TVScreenAnalog(QWidget *parent) : QGLWidget(parent) { m_isDataChanged = false; - m_data = std::make_shared(5, 1); + m_frontBuffer = std::make_shared(5, 1); + m_backBuffer = std::make_shared(5, 1); - connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick())); - m_objTimer.start(40); // capped at 25 FPS + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(tick())); + m_updateTimer.start(40); // capped at 25 FPS } void TVScreenAnalog::cleanup() @@ -81,9 +82,9 @@ void TVScreenAnalog::cleanup() m_lineShiftsTexture = nullptr; } -std::shared_ptr TVScreenAnalog::getData() +std::shared_ptr TVScreenAnalog::getBackBuffer() { - return m_data; + return m_backBuffer; } void TVScreenAnalog::resizeTVScreen(int intCols, int intRows) @@ -91,8 +92,12 @@ void TVScreenAnalog::resizeTVScreen(int intCols, int intRows) qDebug("TVScreen::resizeTVScreen: cols: %d, rows: %d", intCols, intRows); int colsAdj = intCols + 4; - if (m_data->getWidth() != colsAdj || m_data->getHeight() != intRows) - m_data = std::make_shared(colsAdj, intRows); + QMutexLocker lock(&m_buffersMutex); + if (m_frontBuffer->getWidth() != colsAdj || m_frontBuffer->getHeight() != intRows) + { + m_frontBuffer = std::make_shared(colsAdj, intRows); + m_backBuffer = std::make_shared(colsAdj, intRows); + } } void TVScreenAnalog::resizeGL(int intWidth, int intHeight) @@ -143,12 +148,12 @@ void TVScreenAnalog::initializeGL() m_texelHeightLoc = m_shader->uniformLocation("tlh"); } -void TVScreenAnalog::initializeTextures() +void TVScreenAnalog::initializeTextures(std::shared_ptr buffer) { m_imageTexture = std::make_shared(QOpenGLTexture::Target2D); m_lineShiftsTexture = std::make_shared(QOpenGLTexture::Target2D); - m_imageTexture->setSize(m_data->getWidth(), m_data->getHeight()); - m_lineShiftsTexture->setSize(1, m_data->getHeight()); + m_imageTexture->setSize(buffer->getWidth(), buffer->getHeight()); + m_lineShiftsTexture->setSize(1, buffer->getHeight()); m_imageTexture->setFormat(QOpenGLTexture::RGBA8_UNorm); m_lineShiftsTexture->setFormat(QOpenGLTexture::RGBA8_UNorm); m_imageTexture->setAutoMipMapGenerationEnabled(false); @@ -166,9 +171,12 @@ void TVScreenAnalog::initializeTextures() m_lineShiftsTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); } -void TVScreenAnalog::renderImage() +std::shared_ptr TVScreenAnalog::swapBuffers() { + QMutexLocker lock(&m_buffersMutex); + std::swap(m_frontBuffer, m_backBuffer); m_isDataChanged = true; + return m_backBuffer; } void TVScreenAnalog::tick() @@ -190,15 +198,17 @@ void TVScreenAnalog::paintGL() return; } + std::shared_ptr buffer = m_frontBuffer; + if (!m_imageTexture || - m_imageTexture->width() != m_data->getWidth() || - m_imageTexture->height() != m_data->getHeight()) + m_imageTexture->width() != buffer->getWidth() || + m_imageTexture->height() != buffer->getHeight()) { - initializeTextures(); + initializeTextures(buffer); } - float imageWidth = m_data->getWidth(); - float imageHeight = m_data->getHeight(); + float imageWidth = buffer->getWidth(); + float imageHeight = buffer->getHeight(); float texelWidth = 1.0f / imageWidth; float texelHeight = 1.0f / imageHeight; @@ -213,12 +223,12 @@ void TVScreenAnalog::paintGL() glActiveTexture(GL_TEXTURE0); m_imageTexture->bind(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - m_data->getWidth(), m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getImageData()); + buffer->getWidth(), buffer->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, buffer->getImageData()); glActiveTexture(GL_TEXTURE1); m_lineShiftsTexture->bind(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - 1, m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getLineShiftData()); + 1, buffer->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, buffer->getLineShiftData()); float rectHalfWidth = 1.0f + 4.0f / (imageWidth - 4.0f); GLfloat vertices[] = diff --git a/sdrgui/gui/tvscreenanalog.h b/sdrgui/gui/tvscreenanalog.h index 080eda2f8..c85bebd0b 100644 --- a/sdrgui/gui/tvscreenanalog.h +++ b/sdrgui/gui/tvscreenanalog.h @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -36,10 +35,10 @@ #include #include -class TVScreenAnalogData +class TVScreenAnalogBuffer { public: - TVScreenAnalogData(int width, int height) + TVScreenAnalogBuffer(int width, int height) { m_width = width; m_height = height; @@ -53,7 +52,7 @@ public: std::fill(m_lineShiftData, m_lineShiftData + height, 127); } - ~TVScreenAnalogData() + ~TVScreenAnalogBuffer() { delete[] m_imageData; delete[] m_lineShiftData; @@ -116,7 +115,7 @@ class SDRGUI_API TVScreenAnalog : public QGLWidget, protected QOpenGLFunctions { Q_OBJECT - QTimer m_objTimer; + QTimer m_updateTimer; bool m_isDataChanged; @@ -129,7 +128,9 @@ class SDRGUI_API TVScreenAnalog : public QGLWidget, protected QOpenGLFunctions int m_vertexAttribIndex; int m_texCoordAttribIndex; - std::shared_ptr m_data; + QMutex m_buffersMutex; + std::shared_ptr m_frontBuffer; + std::shared_ptr m_backBuffer; std::shared_ptr m_shader; std::shared_ptr m_imageTexture; @@ -138,12 +139,12 @@ class SDRGUI_API TVScreenAnalog : public QGLWidget, protected QOpenGLFunctions public: TVScreenAnalog(QWidget *parent); - std::shared_ptr getData(); + std::shared_ptr getBackBuffer(); + std::shared_ptr swapBuffers(); void resizeTVScreen(int intCols, int intRows); - void renderImage(); private: - void initializeTextures(); + void initializeTextures(std::shared_ptr buffer); void initializeGL() override; void paintGL() override; void resizeGL(int width, int height);