diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 212613c34c8..9cab55a95f4 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -5,18 +5,21 @@ #include #include #include -#include #include #include #include +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.h" #include "util/assert.h" #include "util/roundtopixel.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/vertexdata.h" // Render digits using a texture (generated) with digits with blurred dark outline +using namespace rendergraph; + namespace { // The texture will contain 12 characters: 10 digits, colon and dot @@ -57,23 +60,22 @@ static_assert(checkCharToIndex()); } // namespace -allshader::DigitsRenderer::~DigitsRenderer() = default; - -void allshader::DigitsRenderer::init() { - initializeOpenGLFunctions(); - m_shader.init(); +allshader::DigitsRenderNode::DigitsRenderNode() { + setGeometry(std::make_unique(TextureMaterial::attributes(), 0)); + setMaterial(std::make_unique()); + geometry().setDrawingMode(Geometry::DrawingMode::Triangles); } -float allshader::DigitsRenderer::height() const { +allshader::DigitsRenderNode::~DigitsRenderNode() = default; + +float allshader::DigitsRenderNode::height() const { return m_height; } -void allshader::DigitsRenderer::updateTexture( - float fontPointSize, float maxHeight, float devicePixelRatio) { - if (std::lround(maxHeight * devicePixelRatio) <= 0) { - return; - } - +void allshader::DigitsRenderNode::updateTexture(rendergraph::Context* pContext, + float fontPointSize, + float maxHeight, + float devicePixelRatio) { if (fontPointSize == m_fontPointSize && maxHeight == m_maxHeight) { return; } @@ -184,12 +186,12 @@ void allshader::DigitsRenderer::updateTexture( blur->setBlurRadius(static_cast(m_penWidth) / 3); QGraphicsScene scene; - auto item = std::make_unique(); - item->setPixmap(QPixmap::fromImage(image)); - item->setGraphicsEffect(blur.release()); + QGraphicsPixmapItem item; + item.setPixmap(QPixmap::fromImage(image)); + item.setGraphicsEffect(blur.release()); image.fill(Qt::transparent); QPainter painter(&image); - scene.addItem(item.release()); + scene.addItem(&item); scene.render(&painter, QRectF(), QRectF(0, 0, image.width(), image.height())); } @@ -210,64 +212,66 @@ void allshader::DigitsRenderer::updateTexture( painter.drawPath(path); } - m_texture.setData(image); + dynamic_cast(material()) + .setTexture(std::make_unique(pContext, image)); } -float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, +void allshader::DigitsRenderNode::update( + float x, + float y, + bool multiLine, + const QString& s1, + const QString& s2) { + const int numVerticesPerRectangle = 6; + const int reserved = (s1.length() + s2.length()) * numVerticesPerRectangle; + geometry().allocate(reserved); + TexturedVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + + const float ch = height(); + if (!s1.isEmpty()) { + const auto w = addVertices(vertexUpdater, + x, + y, + s1); + if (multiLine) { + y += ch; + } else { + x += w + ch * 0.75f; + } + } + if (!s2.isEmpty()) { + addVertices(vertexUpdater, + x, + y, + s2); + } + + DEBUG_ASSERT(reserved == vertexUpdater.index()); +} + +void allshader::DigitsRenderNode::clear() { + geometry().allocate(0); +} + +float allshader::DigitsRenderNode::addVertices(TexturedVertexUpdater& vertexUpdater, float x, float y, const QString& s) { - const int n = s.length(); const float x0 = x; const float space = static_cast(m_penWidth) / 2; - VertexData posVertices; - VertexData texVertices; - - posVertices.reserve(n * 6); // two triangles per character - texVertices.reserve(n * 6); - for (QChar c : s) { if (x != x0) { x -= space; } int index = charToIndex(c); - texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); - posVertices.addRectangle(x, - y, - x + m_width[index], - y + height()); + vertexUpdater.addRectangle({x, y}, + {x + m_width[index], y + height()}, + {m_offset[index], 0.f}, + {m_offset[index + 1], 1.f}); x += m_width[index]; } - m_shader.bind(); - - const int matrixLocation = m_shader.uniformLocation("matrix"); - const int textureLocation = m_shader.uniformLocation("texture"); - const int positionLocation = m_shader.attributeLocation("position"); - const int texcoordLocation = m_shader.attributeLocation("texcoord"); - - m_shader.setUniformValue(matrixLocation, matrix); - - m_shader.enableAttributeArray(positionLocation); - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, posVertices.constData(), 2); - m_shader.enableAttributeArray(texcoordLocation); - m_shader.setAttributeArray( - texcoordLocation, GL_FLOAT, texVertices.constData(), 2); - - m_shader.setUniformValue(textureLocation, 0); - - m_texture.bind(); - - glDrawArrays(GL_TRIANGLES, 0, posVertices.size()); - - m_texture.release(); - - m_shader.disableAttributeArray(positionLocation); - m_shader.disableAttributeArray(texcoordLocation); - m_shader.release(); - return x - x0; } diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 4d280bbdaad..9e7c1884e45 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -1,30 +1,44 @@ #pragma once -#include +#include "rendergraph/context.h" +#include "rendergraph/geometrynode.h" +#include "util/class.h" -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" +namespace rendergraph { +class TexturedVertexUpdater; +} // namespace rendergraph namespace allshader { -class DigitsRenderer; -} +class DigitsRenderNode; +} // namespace allshader -class allshader::DigitsRenderer : public QOpenGLFunctions { +class allshader::DigitsRenderNode : public rendergraph::GeometryNode { public: - DigitsRenderer() = default; - ~DigitsRenderer(); + DigitsRenderNode(); + ~DigitsRenderNode(); - void init(); - void updateTexture(float fontPointSize, float maxHeight, float devicePixelRatio); - float draw(const QMatrix4x4& matrix, + void updateTexture(rendergraph::Context* pContext, + float fontPointSize, + float maxHeight, + float devicePixelRatio); + + void update( float x, float y, - const QString& s); + bool multiLine, + const QString& s1, + const QString& s2); + + void clear(); + float height() const; private: - mixxx::TextureShader m_shader; - OpenGLTexture2D m_texture; + float addVertices(rendergraph::TexturedVertexUpdater& vertexUpdater, + float x, + float y, + const QString& s); + int m_penWidth; float m_offset[13]; float m_width[12]; @@ -32,5 +46,5 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { float m_height{}; float m_maxHeight{}; float m_adjustedFontPointSize{}; - DISALLOW_COPY_AND_ASSIGN(DigitsRenderer); + DISALLOW_COPY_AND_ASSIGN(DigitsRenderNode); }; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 2f85b24149c..b79574578ac 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -1,17 +1,24 @@ #include "waveform/renderers/allshader/waveformrendermark.h" -#include #include +#include "rendergraph/context.h" +#include "rendergraph/geometry.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/material/texturematerial.h" +#include "rendergraph/texture.h" +#include "rendergraph/vertexupdaters/rgbavertexupdater.h" +#include "rendergraph/vertexupdaters/texturedvertexupdater.h" #include "track/track.h" #include "util/colorcomponents.h" #include "util/roundtopixel.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/rgbadata.h" -#include "waveform/renderers/allshader/vertexdata.h" +#include "waveform/renderers/allshader/digitsrenderer.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" +using namespace rendergraph; + // On the use of QPainter: // // The renderers in this folder are optimized to use GLSL shaders and refrain @@ -23,17 +30,83 @@ namespace { -class TextureGraphics : public WaveformMark::Graphics { +class WaveformMarkNode : public rendergraph::GeometryNode { public: - TextureGraphics(const QImage& image) { - m_texture.setData(image); + 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) { +#ifdef MIXXX_DEBUG_ASSERTIONS_ENABLED + const float epsilon = 1e-6f; + auto roundToPixel = createFunctionRoundToPixel(devicePixelRatio); + DEBUG_ASSERT(std::abs(x - roundToPixel(x)) < epsilon); + DEBUG_ASSERT(std::abs(y - roundToPixel(y)) < epsilon); +#endif + TexturedVertexUpdater vertexUpdater{ + geometry().vertexDataAs()}; + vertexUpdater.addRectangle({x, y}, + {x + m_textureWidth / devicePixelRatio, + y + m_textureHeight / devicePixelRatio}, + {0.f, 0.f}, + {1.f, 1.f}); } - QOpenGLTexture* texture() { - return &m_texture; + 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 attachNode(std::unique_ptr pNode) { + DEBUG_ASSERT(!m_pNode); + m_pNode = std::move(pNode); + } + std::unique_ptr detachNode() { + return std::move(m_pNode); } private: - OpenGLTexture2D m_texture; + WaveformMarkNode* waveformMarkNode() const { + DEBUG_ASSERT(m_pNode); + return static_cast(m_pNode.get()); + } + + std::unique_ptr m_pNode; }; constexpr float kPlayPosWidth{11.f}; @@ -70,87 +143,49 @@ allshader::WaveformRenderMark::WaveformRenderMark( m_beatsUntilMark(0), m_timeUntilMark(0.0), m_pTimeRemainingControl(nullptr), - m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { -} + m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip), + m_playPosHeight(0.f), + m_playPosDevicePixelRatio(0.f) { + { + auto pNode = std::make_unique(); + m_pRangeNodesParent = pNode.get(); + appendChildNode(std::move(pNode)); + } -bool allshader::WaveformRenderMark::init() { - m_pTimeRemainingControl = std::make_unique( - m_waveformRenderer->getGroup(), "time_remaining"); - return true; + { + auto pNode = std::make_unique(); + m_pMarkNodesParent = pNode.get(); + appendChildNode(std::move(pNode)); + } + + { + auto pNode = std::make_unique(); + m_pDigitsRenderNode = pNode.get(); + appendChildNode(std::move(pNode)); + } + + { + auto pNode = std::make_unique(); + m_pPlayPosNode = pNode.get(); + m_pPlayPosNode->initForRectangles(1); + appendChildNode(std::move(pNode)); + } } void allshader::WaveformRenderMark::draw(QPainter*, QPaintEvent*) { DEBUG_ASSERT(false); } -void allshader::WaveformRenderMark::initializeGL() { - m_digitsRenderer.init(); - m_rgbaShader.init(); - m_textureShader.init(); - - // Will create textures so requires OpenGL context - updateMarkImages(); - updatePlayPosMarkTexture(); - const auto untilMarkTextPointSize = - WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); - const auto untilMarkTextHeightLimit = - WaveformWidgetFactory::instance() - ->getUntilMarkTextHeightLimit(); // proportion of waveform - // height - const auto untilMarkMaxHeightForText = getMaxHeightForText(untilMarkTextHeightLimit); - - m_digitsRenderer.updateTexture(untilMarkTextPointSize, - untilMarkMaxHeightForText, - m_waveformRenderer->getDevicePixelRatio()); -} - -void allshader::WaveformRenderMark::drawTexture( - const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* pTexture) { - const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const float texx1 = 0.f; - const float texy1 = 0.f; - const float texx2 = 1.f; - const float texy2 = 1.f; - - const float posx1 = x; - const float posx2 = x + static_cast(pTexture->width() / devicePixelRatio); - const float posy1 = y; - const float posy2 = y + static_cast(pTexture->height() / devicePixelRatio); - - const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; - const float texarray[] = {texx1, texy1, texx2, texy1, texx1, texy2, texx2, texy2}; - - m_textureShader.bind(); - - const int matrixLocation = m_textureShader.uniformLocation("matrix"); - const int textureLocation = m_textureShader.uniformLocation("texture"); - const int positionLocation = m_textureShader.attributeLocation("position"); - const int texcoordLocation = m_textureShader.attributeLocation("texcoord"); - - m_textureShader.setUniformValue(matrixLocation, matrix); - - m_textureShader.enableAttributeArray(positionLocation); - m_textureShader.setAttributeArray( - positionLocation, GL_FLOAT, posarray, 2); - m_textureShader.enableAttributeArray(texcoordLocation); - m_textureShader.setAttributeArray( - texcoordLocation, GL_FLOAT, texarray, 2); - - m_textureShader.setUniformValue(textureLocation, 0); - - pTexture->bind(); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - pTexture->release(); - - m_textureShader.disableAttributeArray(positionLocation); - m_textureShader.disableAttributeArray(texcoordLocation); - m_textureShader.release(); +bool allshader::WaveformRenderMark::init() { + m_pTimeRemainingControl = std::make_unique( + m_waveformRenderer->getGroup(), "time_remaining"); + ::WaveformRenderMarkBase::init(); + return true; } -void allshader::WaveformRenderMark::drawMark( - const QMatrix4x4& matrix, const QRectF& rect, QColor color) { +void allshader::WaveformRenderMark::updateRangeNode(GeometryNode* pNode, + const QRectF& rect, + QColor color) { // draw a gradient towards transparency at the upper and lower 25% of the waveform view const float qh = static_cast(std::floor(rect.height() * 0.25)); @@ -165,43 +200,41 @@ void allshader::WaveformRenderMark::drawMark( getRgbF(color, &r, &g, &b, &a); - VertexData vertices; - vertices.reserve(12); // 4 triangles - vertices.addRectangle(posx1, posy1, posx2, posy2); - vertices.addRectangle(posx1, posy4, posx2, posy3); - - RGBAData rgbaData; - rgbaData.reserve(12); // 4 triangles - rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - - m_rgbaShader.bind(); - - const int matrixLocation = m_rgbaShader.matrixLocation(); - const int positionLocation = m_rgbaShader.positionLocation(); - const int colorLocation = m_rgbaShader.colorLocation(); - - m_rgbaShader.setUniformValue(matrixLocation, matrix); - - m_rgbaShader.enableAttributeArray(positionLocation); - m_rgbaShader.setAttributeArray( - positionLocation, GL_FLOAT, vertices.constData(), 2); - m_rgbaShader.enableAttributeArray(colorLocation); - m_rgbaShader.setAttributeArray( - colorLocation, GL_FLOAT, rgbaData.constData(), 4); - - glDrawArrays(GL_TRIANGLES, 0, vertices.size()); + RGBAVertexUpdater vertexUpdater{pNode->geometry().vertexDataAs()}; + vertexUpdater.addRectangleVGradient( + {posx1, posy1}, {posx2, posy2}, {r, g, b, a}, {r, g, b, 0.f}); + vertexUpdater.addRectangleVGradient( + {posx1, posy4}, {posx2, posy3}, {r, g, b, a}, {r, g, b, 0.f}); +} - m_rgbaShader.disableAttributeArray(positionLocation); - m_rgbaShader.disableAttributeArray(colorLocation); - m_rgbaShader.release(); +bool allshader::WaveformRenderMark::isSubtreeBlocked() const { + return m_isSlipRenderer && !m_waveformRenderer->isSlipActive(); } -void allshader::WaveformRenderMark::paintGL() { - if (m_isSlipRenderer && !m_waveformRenderer->isSlipActive()) { +void allshader::WaveformRenderMark::update() { + if (isSubtreeBlocked()) { return; } + // For each WaveformMark we create a GeometryNode with Texture + // (in updateMarkImage). Of these GeometryNodes, we append the + // the ones that need to be shown on screen as children to + // m_pMarkNodesParent (transferring ownership). + // + // At the beginning of a new frame, we remove all the child nodes + // from m_pMarkNodesParent and store each with their mark + // (transferring ownership). Later in this function we move the + // visible nodes back to m_pMarkNodesParent children. + while (auto pChild = m_pMarkNodesParent->firstChild()) { + auto pNode = m_pMarkNodesParent->detachChildNode(pChild); + WaveformMarkNode* pWaveformMarkNode = static_cast(pNode.get()); + // Determine its WaveformMark + auto pMark = pWaveformMarkNode->m_pOwner; + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + // Store the node with the WaveformMark + pGraphics->attachNode(std::move(pNode)); + } + auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip : ::WaveformRendererAbstract::Play; bool slipActive = m_waveformRenderer->isSlipActive(); @@ -209,23 +242,25 @@ void allshader::WaveformRenderMark::paintGL() { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); QList marksOnScreen; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - auto roundToPixel = createFunctionRoundToPixel(devicePixelRatio); for (const auto& pMark : std::as_const(m_marks)) { pMark->setBreadth(slipActive ? m_waveformRenderer->getBreadth() / 2 : m_waveformRenderer->getBreadth()); } - // Will create textures so requires OpenGL context - updateMarkImages(); - QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); + updatePlayPosMarkTexture(m_waveformRenderer->getContext()); + + // Generate initial node or update its texture if needed for each of + // the WaveformMarks (in which case updateMarkImage is called) + // (Will create textures so requires OpenGL context) + updateMarkImages(); const double playPosition = m_waveformRenderer->getTruePosSample(positionType); double nextMarkPosition = std::numeric_limits::max(); + GeometryNode* pRangeChild = static_cast(m_pRangeNodesParent->firstChild()); + for (const auto& pMark : std::as_const(m_marks)) { if (!pMark->isValid()) { continue; @@ -237,18 +272,10 @@ void allshader::WaveformRenderMark::paintGL() { continue; } - QOpenGLTexture* pTexture = - static_cast(pMark->m_pGraphics.get()) - ->texture(); - - if (!pTexture) { - continue; - } - - if (!pTexture->isCreated()) { - // This happens if the height is zero + auto pMarkGraphics = pMark->m_pGraphics.get(); + auto pMarkNodeGraphics = static_cast(pMarkGraphics); + if (!pMarkGraphics) // is this even possible? continue; - } const float currentMarkPos = static_cast( m_waveformRenderer->transformSamplePositionInRendererWorld( @@ -260,19 +287,23 @@ void allshader::WaveformRenderMark::paintGL() { } const double sampleEndPosition = pMark->getSampleEndPosition(); - const float markWidth = pTexture->width() / devicePixelRatio; + const float markWidth = pMarkNodeGraphics->textureWidth() / devicePixelRatio; const float drawOffset = currentMarkPos + pMark->getOffset(); bool visible = false; // Check if the current point needs to be displayed. if (drawOffset > -markWidth && drawOffset < m_waveformRenderer->getLength()) { - drawTexture(matrix, + pMarkNodeGraphics->update( roundToPixel(drawOffset), !m_isSlipRenderer && slipActive ? roundToPixel(m_waveformRenderer->getBreadth() / 2.f) : 0, - pTexture); + devicePixelRatio); + + // transfer back to m_pMarkNodesParent children, for rendering + m_pMarkNodesParent->appendChildNode(pMarkNodeGraphics->detachNode()); + visible = true; } @@ -286,13 +317,22 @@ void allshader::WaveformRenderMark::paintGL() { QColor color = pMark->fillColor(); color.setAlphaF(0.4f); - drawMark(matrix, - QRectF(QPointF(roundToPixel(currentMarkPos), 0), + // Reuse, or create new when needed + if (!pRangeChild) { + auto pNode = std::make_unique(); + pNode->initForRectangles(2); + pRangeChild = pNode.get(); + m_pRangeNodesParent->appendChildNode(std::move(pNode)); + } + + updateRangeNode(pRangeChild, + QRectF(QPointF(roundToPixel(currentMarkPos), 0.f), QPointF(roundToPixel(currentMarkEndPos), - m_waveformRenderer - ->getBreadth())), + roundToPixel(m_waveformRenderer->getBreadth()))), color); + visible = true; + pRangeChild = static_cast(pRangeChild->nextSibling()); } } @@ -302,24 +342,36 @@ void allshader::WaveformRenderMark::paintGL() { pMark, static_cast(drawOffset)}); } } + + // Remove unused nodes + while (pRangeChild) { + auto pNode = m_pRangeNodesParent->detachChildNode(pRangeChild); + pRangeChild = static_cast(pRangeChild->nextSibling()); + } + m_waveformRenderer->setMarkPositions(marksOnScreen); const float playMarkerPos = static_cast(m_waveformRenderer->getPlayMarkerPosition() * m_waveformRenderer->getLength()); - if (m_playPosMarkTexture.isStorageAllocated()) { + { const float drawOffset = roundToPixel(playMarkerPos + kPlayPosOffset); - - drawTexture(matrix, drawOffset, 0.f, &m_playPosMarkTexture); + TexturedVertexUpdater vertexUpdater{ + m_pPlayPosNode->geometry() + .vertexDataAs()}; + vertexUpdater.addRectangle({drawOffset, 0.f}, + {drawOffset + kPlayPosWidth, static_cast(m_waveformRenderer->getBreadth())}, + {0.f, 0.f}, + {1.f, 1.f}); } if (WaveformWidgetFactory::instance()->getUntilMarkShowBeats() || WaveformWidgetFactory::instance()->getUntilMarkShowTime()) { updateUntilMark(playPosition, nextMarkPosition); - drawUntilMark(matrix, roundToPixel(playMarkerPos + 20.f)); + updateDigitsNodeForUntilMark(roundToPixel(playMarkerPos + 20.f)); } } -void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, float x) { +void allshader::WaveformRenderMark::updateDigitsNodeForUntilMark(float x) { const bool untilMarkShowBeats = WaveformWidgetFactory::instance()->getUntilMarkShowBeats(); const bool untilMarkShowTime = WaveformWidgetFactory::instance()->getUntilMarkShowTime(); const auto untilMarkAlign = WaveformWidgetFactory::instance()->getUntilMarkAlign(); @@ -332,14 +384,16 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa // height const auto untilMarkMaxHeightForText = getMaxHeightForText(untilMarkTextHeightLimit); - m_digitsRenderer.updateTexture(untilMarkTextPointSize, + m_pDigitsRenderNode->updateTexture(m_waveformRenderer->getContext(), + untilMarkTextPointSize, untilMarkMaxHeightForText, m_waveformRenderer->getDevicePixelRatio()); if (m_timeUntilMark == 0.0) { + m_pDigitsRenderNode->clear(); return; } - const float ch = m_digitsRenderer.height(); + const float ch = m_pDigitsRenderNode->height(); float y = untilMarkAlign == Qt::AlignTop ? 0.f : untilMarkAlign == Qt::AlignBottom @@ -360,40 +414,35 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa } } - if (untilMarkShowBeats) { - const auto w = m_digitsRenderer.draw(matrix, - x, - y, - QString::number(m_beatsUntilMark)); - if (multiLine) { - y += ch; - } else { - x += w + ch * 0.75f; - } - } - - if (untilMarkShowTime) { - m_digitsRenderer.draw(matrix, - x, - y, - timeSecToString(m_timeUntilMark)); - } + m_pDigitsRenderNode->update( + x, + y, + multiLine, + untilMarkShowBeats ? QString::number(m_beatsUntilMark) : QString{}, + untilMarkShowTime ? timeSecToString(m_timeUntilMark) : QString{}); } // Generate the texture used to draw the play position marker. // Note that in the legacy waveform widgets this is drawn directly // in the WaveformWidgetRenderer itself. Doing it here is cleaner. -void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { - const float imgHeight = m_waveformRenderer->getBreadth(); - const float imgWidth = kPlayPosWidth; +void allshader::WaveformRenderMark::updatePlayPosMarkTexture(rendergraph::Context* pContext) { + float imgWidth; + float imgHeight; - if (imgHeight == 0.0f) { + const float height = m_waveformRenderer->getBreadth(); + const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); + + if (height == m_playPosHeight && devicePixelRatio == m_playPosDevicePixelRatio) { return; } + m_playPosHeight = height; + m_playPosDevicePixelRatio = devicePixelRatio; - const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float lineX = 5.5f; + imgWidth = kPlayPosWidth; + imgHeight = height; + const QSize size{static_cast(std::lround(imgWidth * devicePixelRatio)), static_cast(std::lround(imgHeight * devicePixelRatio))}; @@ -451,7 +500,8 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { } painter.end(); - m_playPosMarkTexture.setData(image); + dynamic_cast(m_pPlayPosNode->material()) + .setTexture(std::make_unique(pContext, image)); } void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, @@ -468,15 +518,19 @@ void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, painter->fillPath(triangle, fillColor); } -void allshader::WaveformRenderMark::resizeGL(int, int) { - // Will create textures so requires OpenGL context - updateMarkImages(); - updatePlayPosMarkTexture(); -} - void allshader::WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { - pMark->m_pGraphics = std::make_unique( - pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); + if (!pMark->m_pGraphics) { + pMark->m_pGraphics = + std::make_unique(pMark.get(), + m_waveformRenderer->getContext(), + pMark->generateImage( + m_waveformRenderer->getDevicePixelRatio())); + } else { + auto pGraphics = static_cast(pMark->m_pGraphics.get()); + pGraphics->updateTexture(m_waveformRenderer->getContext(), + pMark->generateImage( + m_waveformRenderer->getDevicePixelRatio())); + } } void allshader::WaveformRenderMark::updateUntilMark( diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index eba21888e3c..511dbaa8993 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -2,40 +2,42 @@ #include -#include "rendergraph/openglnode.h" -#include "shaders/rgbashader.h" -#include "shaders/textureshader.h" -#include "util/opengltexture2d.h" -#include "waveform/renderers/allshader/digitsrenderer.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/node.h" #include "waveform/renderers/waveformrendermarkbase.h" class QDomNode; -class SkinContext; -class QOpenGLTexture; + +namespace rendergraph { +class GeometryNode; +class Context; +} // namespace rendergraph namespace allshader { +class DigitsRenderNode; class WaveformRenderMark; -} +} // namespace allshader -class allshader::WaveformRenderMark final - : public ::WaveformRenderMarkBase, - public rendergraph::OpenGLNode { +class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, + public rendergraph::Node { public: explicit WaveformRenderMark(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + bool init() override; - void draw(QPainter* painter, QPaintEvent* event) override; - void initializeGL() override; - void paintGL() override; - void resizeGL(int w, int h) override; + void update(); + + bool isSubtreeBlocked() const override; private: void updateMarkImage(WaveformMarkPointer pMark) override; - void updatePlayPosMarkTexture(); + void updatePlayPosMarkTexture(rendergraph::Context* pContext); void drawTriangle(QPainter* painter, const QBrush& fillColor, @@ -43,16 +45,13 @@ class allshader::WaveformRenderMark final QPointF p2, QPointF p3); - void drawMark(const QMatrix4x4& matrix, const QRectF& rect, QColor color); - void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* pTexture); void updateUntilMark(double playPosition, double markerPosition); - void drawUntilMark(const QMatrix4x4& matrix, float x); + void updateDigitsNodeForUntilMark(float x); float getMaxHeightForText(float proportion) const; + void updateRangeNode(rendergraph::GeometryNode* pNode, + const QRectF& rect, + QColor color); - mixxx::RGBAShader m_rgbaShader; - mixxx::TextureShader m_textureShader; - OpenGLTexture2D m_playPosMarkTexture; - DigitsRenderer m_digitsRenderer; int m_beatsUntilMark; double m_timeUntilMark; double m_currentBeatPosition; @@ -61,5 +60,14 @@ class allshader::WaveformRenderMark final bool m_isSlipRenderer; + rendergraph::Node* m_pRangeNodesParent{}; + rendergraph::Node* m_pMarkNodesParent{}; + + rendergraph::GeometryNode* m_pPlayPosNode; + float m_playPosHeight; + float m_playPosDevicePixelRatio; + + DigitsRenderNode* m_pDigitsRenderNode{}; + DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index d01d73b795f..c40af8927d9 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -5,10 +5,10 @@ #include "control/controlproxy.h" #include "track/cue.h" +#include "waveform/renderers/waveformsignalcolors.h" #include "waveform/waveformmarklabel.h" class SkinContext; -class WaveformSignalColors; class QOpenGLTexture; namespace allshader { diff --git a/src/waveform/renderers/waveformmarkrange.h b/src/waveform/renderers/waveformmarkrange.h index 15100bf8f03..5ac530e86af 100644 --- a/src/waveform/renderers/waveformmarkrange.h +++ b/src/waveform/renderers/waveformmarkrange.h @@ -56,7 +56,7 @@ class WaveformMarkRange { WaveformMarkLabel m_durationLabel; private: - void generateImage(int weidth, int height); + void generateImage(int width, int height); std::unique_ptr m_markStartPointControl; std::unique_ptr m_markEndPointControl; diff --git a/src/waveform/renderers/waveformrendermarkbase.cpp b/src/waveform/renderers/waveformrendermarkbase.cpp index f250b73acc1..b2293d1448d 100644 --- a/src/waveform/renderers/waveformrendermarkbase.cpp +++ b/src/waveform/renderers/waveformrendermarkbase.cpp @@ -14,9 +14,13 @@ WaveformRenderMarkBase::WaveformRenderMarkBase( void WaveformRenderMarkBase::setup(const QDomNode& node, const SkinContext& context) { WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); +} + +bool WaveformRenderMarkBase::init() { m_marks.connectSamplePositionChanged(this, &WaveformRenderMarkBase::onMarkChanged); m_marks.connectSampleEndPositionChanged(this, &WaveformRenderMarkBase::onMarkChanged); m_marks.connectVisibleChanged(this, &WaveformRenderMarkBase::onMarkChanged); + return true; } void WaveformRenderMarkBase::onSetTrack() { diff --git a/src/waveform/renderers/waveformrendermarkbase.h b/src/waveform/renderers/waveformrendermarkbase.h index b5e4d51dd25..8efc35aebd4 100644 --- a/src/waveform/renderers/waveformrendermarkbase.h +++ b/src/waveform/renderers/waveformrendermarkbase.h @@ -17,6 +17,8 @@ class WaveformRenderMarkBase : public QObject, public WaveformRendererAbstract { void setup(const QDomNode& node, const SkinContext& context) override; + bool init() override; + // Called when a new track is loaded. void onSetTrack() override; diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 06ca7f805bb..313411e003b 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -146,6 +146,7 @@ void WaveformWidget::paintGL() { // opacity of 0.f effectively skips the subtree rendering m_pOpacityNode->setOpacity(shouldOnlyDrawBackground() ? 0.f : 1.f); + m_pWaveformRenderMark->update(); m_pWaveformRenderMarkRange->update(); m_pEngine->preprocess();