diff --git a/src/rendergraph/common/rendergraph/nodeinterface.h b/src/rendergraph/common/rendergraph/nodeinterface.h deleted file mode 100644 index 4b2d08f4d49..00000000000 --- a/src/rendergraph/common/rendergraph/nodeinterface.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "backend/basenode.h" - -namespace rendergraph { - -template -class NodeInterface : public T_Node { - public: - void appendChildNode(std::unique_ptr&& pNode) { - T_Node::appendChildNode(pNode.release()); - } - std::unique_ptr detachChildNode(BaseNode* pNode) { - T_Node::removeChildNode(pNode); - return std::unique_ptr(pNode); - } -}; - -} // namespace rendergraph diff --git a/src/rendergraph/opengl/CMakeLists.txt b/src/rendergraph/opengl/CMakeLists.txt index 9501c2b2b15..529ba2efdb7 100644 --- a/src/rendergraph/opengl/CMakeLists.txt +++ b/src/rendergraph/opengl/CMakeLists.txt @@ -62,6 +62,17 @@ target_link_libraries(rendergraph_gl PUBLIC Qt6::Gui Qt6::OpenGL ) +find_package(Microsoft.GSL CONFIG) +if(Microsoft.GSL_FOUND) + target_link_libraries(rendergraph_gl PRIVATE Microsoft.GSL::GSL) +else() + # check if the headers have been installed without cmake config (< 3.1.0) + check_include_file_cxx(gsl/gsl HAVE_GSL_GSL) + if(NOT HAVE_GSL_GSL) + unset(HAVE_GSL_GSL CACHE) # unset cache to re-evaluate this until it succeeds. check_include_file_cxx() has no REQUIRED flag. + message(FATAL_ERROR "ms-gsl development headers (libmsgsl-dev) not found") + endif() +endif() target_compile_definitions(rendergraph_gl PRIVATE rendergraph=rendergraph_gl) # USE_QSHADER_FOR_GL is set in rendergraph/CMakeLists.txt diff --git a/src/rendergraph/opengl/engine.cpp b/src/rendergraph/opengl/engine.cpp index c5202918155..bfdce71431a 100644 --- a/src/rendergraph/opengl/engine.cpp +++ b/src/rendergraph/opengl/engine.cpp @@ -11,6 +11,8 @@ Engine::Engine(std::unique_ptr pRootNode) } Engine::~Engine() { + // Explicitly remove the root node (and tree from the engine before deallocating its vectors) + remove(m_pRootNode.get()); } void Engine::add(BaseNode* pNode) { diff --git a/src/rendergraph/opengl/texture.cpp b/src/rendergraph/opengl/texture.cpp index 1bd10cf7742..88921bde9df 100644 --- a/src/rendergraph/opengl/texture.cpp +++ b/src/rendergraph/opengl/texture.cpp @@ -1,20 +1,49 @@ #include "rendergraph/texture.h" +#include +#include + +#include "rendergraph/assert.h" #include "rendergraph/context.h" using namespace rendergraph; namespace { QImage premultiplyAlpha(const QImage& image) { + // Since the image is passed by const reference, implicit copy cannot be + // used, and this Qimage::bits will return a ref the QImage buffer, which + // may have a shorter lifecycle that the texture buffer. In order to + // workaround this, and because we cannot copy the image as we need to use + // the raw bitmap with an explicit image format, we make a manual copy of + // the buffer + QImage result(image.width(), image.height(), QImage::Format_RGBA8888); + if (image.format() == QImage::Format_RGBA8888_Premultiplied) { + VERIFY_OR_DEBUG_ASSERT(result.sizeInBytes() == image.sizeInBytes()) { + result.fill(QColor(Qt::transparent).rgba()); + return result; + } + std::memcpy(result.bits(), image.bits(), result.sizeInBytes()); + } else { + auto convertedImage = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + VERIFY_OR_DEBUG_ASSERT(result.sizeInBytes() == convertedImage.sizeInBytes()) { + result.fill(QColor(Qt::transparent).rgba()); + return result; + } + std::memcpy(result.bits(), convertedImage.bits(), result.sizeInBytes()); + } + return result; + /* TODO rendergraph ASK @acolombier to try if the following works as well + * (added the .copy()) if (image.format() == QImage::Format_RGBA8888_Premultiplied) { - return QImage(image.bits(), image.width(), image.height(), QImage::Format_RGBA8888); + return QImage(image.bits(), image.width(), image.height(), QImage::Format_RGBA8888).copy(); } return QImage( image.convertToFormat(QImage::Format_RGBA8888_Premultiplied) .bits(), image.width(), image.height(), - QImage::Format_RGBA8888); + QImage::Format_RGBA8888).copy(); + */ } } // namespace diff --git a/src/rendergraph/scenegraph/CMakeLists.txt b/src/rendergraph/scenegraph/CMakeLists.txt index 45c22885c13..52c197e0ef3 100644 --- a/src/rendergraph/scenegraph/CMakeLists.txt +++ b/src/rendergraph/scenegraph/CMakeLists.txt @@ -55,6 +55,17 @@ target_link_libraries(rendergraph_sg PUBLIC Qt6::Qml Qt6::Quick ) +find_package(Microsoft.GSL CONFIG) +if(Microsoft.GSL_FOUND) + target_link_libraries(rendergraph_sg PRIVATE Microsoft.GSL::GSL) +else() + # check if the headers have been installed without cmake config (< 3.1.0) + check_include_file_cxx(gsl/gsl HAVE_GSL_GSL) + if(NOT HAVE_GSL_GSL) + unset(HAVE_GSL_GSL CACHE) # unset cache to re-evaluate this until it succeeds. check_include_file_cxx() has no REQUIRED flag. + message(FATAL_ERROR "ms-gsl development headers (libmsgsl-dev) not found") + endif() +endif() target_compile_definitions(rendergraph_sg PRIVATE rendergraph=rendergraph_sg) target_include_directories(rendergraph_sg PUBLIC . ../common) diff --git a/src/rendergraph/scenegraph/context.cpp b/src/rendergraph/scenegraph/context.cpp index 61c5e12caa7..29c3f567e25 100644 --- a/src/rendergraph/scenegraph/context.cpp +++ b/src/rendergraph/scenegraph/context.cpp @@ -2,10 +2,10 @@ using namespace rendergraph; -Context::Context(QQuickWindow* pWindow) +Context::Context(gsl::not_null pWindow) : m_pWindow(pWindow) { } -QQuickWindow* Context::window() const { +gsl::not_null Context::window() const { return m_pWindow; } diff --git a/src/rendergraph/scenegraph/rendergraph/context.h b/src/rendergraph/scenegraph/rendergraph/context.h index e26e6eb7859..431b41d027b 100644 --- a/src/rendergraph/scenegraph/rendergraph/context.h +++ b/src/rendergraph/scenegraph/rendergraph/context.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace rendergraph { class Context; @@ -8,8 +9,8 @@ class Context; class rendergraph::Context { public: - Context(QQuickWindow* pWindow); - QQuickWindow* window() const; + Context(gsl::not_null pWindow); + gsl::not_null window() const; private: QQuickWindow* m_pWindow; diff --git a/src/rendergraph/scenegraph/rendergraph/nodeinterface.h b/src/rendergraph/scenegraph/rendergraph/nodeinterface.h new file mode 100644 index 00000000000..6bfaebacfbe --- /dev/null +++ b/src/rendergraph/scenegraph/rendergraph/nodeinterface.h @@ -0,0 +1,26 @@ +#pragma once + +#include "backend/basenode.h" +#include "rendergraph/assert.h" + +namespace rendergraph { + +template +class NodeInterface : public T_Node { + public: + void appendChildNode(std::unique_ptr pNode) { + BaseNode* pRawNode = pNode.release(); + pRawNode->setFlag(QSNode::OwnedByParent, true); + T_Node::appendChildNode(pRawNode); + DEBUG_ASSERT(pNode->flags() & QSNode::OwnedByParent); + } + std::unique_ptr detachChildNode(BaseNode* pNode) { + DEBUG_ASSERT(pNode->flags() & QSNode::OwnedByParent); + pNode->setFlag(QSNode::OwnedByParent, false); + T_Node::removeChildNode(pNode); + DEBUG_ASSERT(!pNode->flags() & QSNode::OwnedByParent); + return std::unique_ptr(pNode); + } +}; + +} // namespace rendergraph diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index 42c7bf6c155..aa372f1dc76 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -4,12 +4,16 @@ #include #include +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/vertexupdaters/rgbavertexupdater.h" #include "track/track.h" +#include "util/assert.h" #include "util/math.h" -#include "waveform/renderers/allshader/rgbdata.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" +using namespace rendergraph; + namespace { constexpr int kMaxSupportedStems = 4; } // anonymous namespace @@ -21,64 +25,81 @@ WaveformRendererStem::WaveformRendererStem( ::WaveformRendererAbstract::PositionSource type) : WaveformRendererSignalBase(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + initForRectangles(0); + setUsePreprocess(true); } void WaveformRendererStem::onSetup(const QDomNode& node) { Q_UNUSED(node); } -void WaveformRendererStem::initializeGL() { - m_shader.init(); - m_textureShader.init(); - auto group = m_pEQEnabled->getKey().group; +bool WaveformRendererStem::init() { + auto group = m_waveformRenderer->getGroup(); + VERIFY_OR_DEBUG_ASSERT(!group.isEmpty()) { + return false; + } for (int stemIdx = 1; stemIdx <= kMaxSupportedStems; stemIdx++) { DEBUG_ASSERT(group.endsWith("]")); QString stemGroup = QStringLiteral("%1Stem%2]") .arg(group.left(group.size() - 1), QString::number(stemIdx)); m_pStemGain.emplace_back( - std::make_unique(stemGroup, + std::make_unique(stemGroup, QStringLiteral("volume"))); - m_pStemMute.emplace_back(std::make_unique( + m_pStemMute.emplace_back(std::make_unique( stemGroup, QStringLiteral("mute"))); } + return true; } -void WaveformRendererStem::paintGL() { +void WaveformRendererStem::preprocess() { + if (!preprocessInner()) { + if (geometry().vertexCount() != 0) { + geometry().allocate(0); + markDirtyGeometry(); + } + } +} + +bool WaveformRendererStem::preprocessInner() { TrackPointer pTrack = m_waveformRenderer->getTrackInfo(); + if (!pTrack || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; + return false; } auto stemInfo = pTrack->getStemInfo(); // If this track isn't a stem track, skip the rendering if (stemInfo.isEmpty()) { - return; + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip : ::WaveformRendererAbstract::Play; ConstWaveformPointer waveform = pTrack->getWaveform(); if (waveform.isNull()) { - return; + return false; } const int dataSize = waveform->getDataSize(); if (dataSize <= 1) { - return; + return false; } const WaveformData* data = waveform->data(); if (data == nullptr) { - return; + return false; } // If this waveform doesn't contain stem data, skip the rendering if (!waveform->hasStem()) { - return; + return false; } const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const int length = static_cast(m_waveformRenderer->getLength() * devicePixelRatio); + const int length = static_cast(m_waveformRenderer->getLength()); + const int pixelLength = static_cast(m_waveformRenderer->getLength() * devicePixelRatio); + const float invDevicePixelRatio = 1.f / devicePixelRatio; + const float halfPixelSize = 0.5 / devicePixelRatio; // See waveformrenderersimple.cpp for a detailed explanation of the frame and index calculation const int visualFramesSize = dataSize / 2; @@ -89,14 +110,14 @@ void WaveformRendererStem::paintGL() { // Represents the # of visual frames per horizontal pixel. const double visualIncrementPerPixel = - (lastVisualFrame - firstVisualFrame) / static_cast(length); + (lastVisualFrame - firstVisualFrame) / static_cast(pixelLength); // Per-band gain from the EQ knobs. float allGain(1.0); // applyCompensation = true, as we scale to match filtered.all getGains(&allGain, false, nullptr, nullptr, nullptr); - const float breadth = static_cast(m_waveformRenderer->getBreadth()) * devicePixelRatio; + const float breadth = static_cast(m_waveformRenderer->getBreadth()); const float halfBreadth = breadth / 2.0f; const float heightFactor = allGain * halfBreadth / m_maxValue; @@ -107,22 +128,22 @@ void WaveformRendererStem::paintGL() { const int numVerticesPerLine = 6; // 2 triangles - const int reserved = numVerticesPerLine * (8 * length + 1); + const int reserved = numVerticesPerLine * (8 * pixelLength + 1); - m_vertices.clear(); - m_vertices.reserve(reserved); - m_colors.clear(); - m_colors.reserve(reserved); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); + geometry().allocate(reserved); + markDirtyGeometry(); - m_vertices.addRectangle(0.f, - halfBreadth - 0.5f * devicePixelRatio, - static_cast(length), - m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f * devicePixelRatio); - m_colors.addForRectangle(0.f, 0.f, 0.f, 0.f); + RGBAVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + vertexUpdater.addRectangle({0.f, + halfBreadth - 0.5f}, + {static_cast(length), + m_isSlipRenderer ? halfBreadth : halfBreadth + 0.5f}, + {0.f, 0.f, 0.f, 0.f}); const double maxSamplingRange = visualIncrementPerPixel / 2.0; - for (int visualIdx = 0; visualIdx < length; ++visualIdx) { + for (int visualIdx = 0; visualIdx < pixelLength; ++visualIdx) { for (int stemIdx = 0; stemIdx < 4; stemIdx++) { // Stem is drawn twice with different opacity level, this allow to // see the maximum signal by transparency @@ -139,7 +160,7 @@ void WaveformRendererStem::paintGL() { const int visualIndexStop = std::min(std::max(visualFrameStop, visualFrameStart + 1) * 2, dataSize - 1); - const float fVisualIdx = static_cast(visualIdx); + const float fVisualIdx = static_cast(visualIdx) * invDevicePixelRatio; // Find the max values for current eq in the waveform data. // - Max of left and right @@ -164,43 +185,23 @@ void WaveformRendererStem::paintGL() { } // Lines are thin rectangles - // shawdow - m_vertices.addRectangle(fVisualIdx - 0.5f, - halfBreadth - heightFactor * max, - fVisualIdx + 0.5f, - m_isSlipRenderer ? halfBreadth : halfBreadth + heightFactor * max); - - m_colors.addForRectangle(color_r, color_g, color_b, color_a); + // shadow + vertexUpdater.addRectangle({fVisualIdx - halfPixelSize, + halfBreadth - heightFactor * max}, + {fVisualIdx + halfPixelSize, + m_isSlipRenderer ? halfBreadth : halfBreadth + heightFactor * max}, + {color_r, color_g, color_b, color_a}); } } + xVisualFrame += visualIncrementPerPixel; } - DEBUG_ASSERT(reserved == m_vertices.size()); - DEBUG_ASSERT(reserved == m_colors.size()); - - const QMatrix4x4 matrix; // TODO = m_waveformRenderer->getMatrix(true); - - const int matrixLocation = m_shader.matrixLocation(); - const int positionLocation = m_shader.positionLocation(); - const int colorLocation = m_shader.colorLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); - m_shader.enableAttributeArray(colorLocation); - - m_shader.setUniformValue(matrixLocation, matrix); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, m_vertices.constData(), 2); - m_shader.setAttributeArray( - colorLocation, GL_FLOAT, m_colors.constData(), 4); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + markDirtyMaterial(); - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(colorLocation); - m_shader.release(); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 99728194574..1b8881d0cc6 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -2,12 +2,9 @@ #include -#include "rendergraph/openglnode.h" -#include "shaders/rgbashader.h" -#include "shaders/textureshader.h" +#include "control/pollingcontrolproxy.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "waveform/renderers/allshader/rgbadata.h" -#include "waveform/renderers/allshader/vertexdata.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" class QOpenGLTexture; @@ -18,30 +15,31 @@ class WaveformRendererStem; class allshader::WaveformRendererStem final : public allshader::WaveformRendererSignalBase, - public rendergraph::OpenGLNode { + public rendergraph::GeometryNode { public: explicit WaveformRendererStem(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); - // override ::WaveformRendererSignalBase + // Pure virtual from WaveformRendererSignalBase, not used void onSetup(const QDomNode& node) override; - void initializeGL() override; - void paintGL() override; + bool init() override; - private: - mixxx::RGBAShader m_shader; - mixxx::TextureShader m_textureShader; - VertexData m_vertices; - RGBAData m_colors; + bool supportsSlip() const override { + return true; + } + + // Virtuals for rendergraph::Node + void preprocess() override; + private: bool m_isSlipRenderer; - std::vector> m_pStemGain; - std::vector> m_pStemMute; + std::vector> m_pStemGain; + std::vector> m_pStemMute; - void drawTexture(float x, float y, QOpenGLTexture* texture); + bool preprocessInner(); DISALLOW_COPY_AND_ASSIGN(WaveformRendererStem); }; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index eb1a5c2bc7b..ca657d0b24b 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -18,87 +18,36 @@ using namespace rendergraph; -namespace { -// On the use of QPainter: -// -// The renderers in this folder are optimized to use GLSL shaders and refrain -// from using QPainter on the QOpenGLWindow, which causes degredated performance. -// -// This renderer does use QPainter (indirectly, in WaveformMark::generateImage), but -// only to draw on a QImage. This is only done once when needed and the images are -// then used as textures to be drawn with a GLSL shader. - -class WaveformMarkNode : public rendergraph::GeometryNode { - public: - WaveformMark* m_pOwner{}; - - WaveformMarkNode(WaveformMark* pOwner, rendergraph::Context* pContext, const QImage& image) - : m_pOwner(pOwner) { - initForRectangles(1); - updateTexture(pContext, image); - } - void updateTexture(rendergraph::Context* pContext, const QImage& image) { - dynamic_cast(material()) - .setTexture(std::make_unique(pContext, image)); - m_textureWidth = image.width(); - m_textureHeight = image.height(); - } - void update(float x, float y, float devicePixelRatio) { - TexturedVertexUpdater vertexUpdater{ - geometry().vertexDataAs()}; - vertexUpdater.addRectangle({x, y}, - {x + m_textureWidth / devicePixelRatio, - y + m_textureHeight / devicePixelRatio}, - {0.f, 0.f}, - {1.f, 1.f}); - } - float textureWidth() const { - return m_textureWidth; - } - float textureHeight() const { - return m_textureHeight; - } - - public: - float m_textureWidth{}; - float m_textureHeight{}; -}; - -class WaveformMarkNodeGraphics : public WaveformMark::Graphics { - public: - WaveformMarkNodeGraphics(WaveformMark* pOwner, - rendergraph::Context* pContext, - const QImage& image) - : m_pNode(std::make_unique( - pOwner, pContext, image)) { - } - void updateTexture(rendergraph::Context* pContext, const QImage& image) { - waveformMarkNode()->updateTexture(pContext, image); - } - void update(float x, float y, float devicePixelRatio) { - waveformMarkNode()->update(x, y, devicePixelRatio); - } - float textureWidth() const { - return waveformMarkNode()->textureWidth(); - } - float textureHeight() const { - return waveformMarkNode()->textureHeight(); - } - void setNode(std::unique_ptr&& pNode) { - m_pNode = std::move(pNode); - } - void moveNodeToChildrenOf(rendergraph::Node* pParent) { - pParent->appendChildNode(std::move(m_pNode)); - } - - private: - WaveformMarkNode* waveformMarkNode() const { - return static_cast(m_pNode.get()); - } +allshader::WaveformMarkNode::WaveformMarkNode(WaveformMark* pOwner, + rendergraph::Context* pContext, + const QImage& image) + : m_pOwner(pOwner) { + initForRectangles(1); + updateTexture(pContext, image); +} - std::unique_ptr m_pNode; -}; -} // namespace +void allshader::WaveformMarkNode::updateTexture( + rendergraph::Context* pContext, const QImage& image) { + dynamic_cast(material()) + .setTexture(std::make_unique(pContext, image)); + m_textureWidth = image.width(); + m_textureHeight = image.height(); +} +void allshader::WaveformMarkNode::update(float x, float y, float devicePixelRatio) { + TexturedVertexUpdater vertexUpdater{ + geometry().vertexDataAs()}; + vertexUpdater.addRectangle({x, y}, + {x + m_textureWidth / devicePixelRatio, + y + m_textureHeight / devicePixelRatio}, + {0.f, 0.f}, + {1.f, 1.f}); +} +allshader::WaveformMarkNodeGraphics::WaveformMarkNodeGraphics(WaveformMark* pOwner, + rendergraph::Context* pContext, + const QImage& image) + : m_pNode(std::make_unique( + pOwner, pContext, image)) { +} // Both allshader::WaveformRenderMark and the non-GL ::WaveformRenderMark derive // from WaveformRenderMarkBase. The base-class takes care of updating the marks @@ -226,12 +175,13 @@ void allshader::WaveformRenderMark::update() { // (transferring ownership). Later in this function we move the // visible nodes back to m_pMarkNodesParent children. while (auto pChild = m_pMarkNodesParent->firstChild()) { - auto pNode = castToUniquePtr(m_pMarkNodesParent->detachChildNode(pChild)); + auto pNode = m_pMarkNodesParent->detachChildNode(pChild); + WaveformMarkNode* pWaveformMarkNode = static_cast(pNode.get()); // Determine its WaveformMark - auto pMark = pNode->m_pOwner; + auto pMark = pWaveformMarkNode->m_pOwner; auto pGraphics = static_cast(pMark->m_pGraphics.get()); // Store the node with the WaveformMark - pGraphics->setNode(std::move(pNode)); + pGraphics->attachNode(std::move(pNode)); } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -308,7 +258,7 @@ void allshader::WaveformRenderMark::update() { devicePixelRatio); // transfer back to m_pMarkNodesParent children, for rendering - pMarkNodeGraphics->moveNodeToChildrenOf(m_pMarkNodesParent); + m_pMarkNodesParent->appendChildNode(pMarkNodeGraphics->detachNode()); visible = true; } diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index f18b8559a5e..98cf99c6ec0 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -16,6 +16,8 @@ class Context; namespace allshader { class DigitsRenderNode; class WaveformRenderMark; +class WaveformMarkNode; +class WaveformMarkNodeGraphics; } class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, @@ -71,3 +73,65 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; + +// On the use of QPainter: +// +// The renderers in this folder are optimized to use GLSL shaders and refrain +// from using QPainter on the QOpenGLWindow, which causes degredated performance. +// +// This renderer does use QPainter (indirectly, in WaveformMark::generateImage), but +// only to draw on a QImage. This is only done once when needed and the images are +// then used as textures to be drawn with a GLSL shader. + +class allshader::WaveformMarkNode : public rendergraph::GeometryNode { + public: + WaveformMark* m_pOwner{}; + + WaveformMarkNode(WaveformMark* pOwner, rendergraph::Context* pContext, const QImage& image); + void updateTexture(rendergraph::Context* pContext, const QImage& image); + void update(float x, float y, float devicePixelRatio); + float textureWidth() const { + return m_textureWidth; + } + float textureHeight() const { + return m_textureHeight; + } + + public: + float m_textureWidth{}; + float m_textureHeight{}; +}; + +class allshader::WaveformMarkNodeGraphics : public ::WaveformMark::Graphics { + public: + WaveformMarkNodeGraphics(WaveformMark* pOwner, + rendergraph::Context* pContext, + const QImage& image); + void updateTexture(rendergraph::Context* pContext, const QImage& image) { + waveformMarkNode()->updateTexture(pContext, image); + } + void update(float x, float y, float devicePixelRatio) { + waveformMarkNode()->update(x, y, devicePixelRatio); + } + float textureWidth() const { + return waveformMarkNode()->textureWidth(); + } + float textureHeight() const { + return waveformMarkNode()->textureHeight(); + } + void attachNode(std::unique_ptr pNode) { + DEBUG_ASSERT(!m_pNode); + m_pNode = std::move(pNode); + } + std::unique_ptr detachNode() { + return std::move(m_pNode); + } + + private: + WaveformMarkNode* waveformMarkNode() const { + DEBUG_ASSERT(!m_pNode); + return static_cast(m_pNode.get()); + } + + std::unique_ptr m_pNode; +};