diff --git a/src/VisualizerWidget.cpp b/src/VisualizerWidget.cpp index 78a5357..5af33e3 100644 --- a/src/VisualizerWidget.cpp +++ b/src/VisualizerWidget.cpp @@ -384,49 +384,11 @@ void VisualizerWidget::render(QRhiCommandBuffer *cb) { int w = width(); int h = height(); - // Rebuild vertices when new data arrived or widget was resized - if (m_dataDirty || w != m_lastBuildW || h != m_lastBuildH) { + // Only rebuild vertices when new data has arrived + if (m_dataDirty) { m_dataDirty = false; - m_lastBuildW = w; - m_lastBuildH = h; if (m_mirrored) { buildVertices(w * 0.55f, h / 2); - - // Expand half-size vertices into all 4 quadrants (eliminates mirror seam) - { - int fillFloats = m_fillVertexCount * 6; - int lineFloats = m_lineVertexCount * 6; - float fw = (float)w, fh = (float)h; - - auto expand = [&](const float *src, int nVerts, std::vector &dst) { - for (int q = 0; q < 4; q++) { - bool fx = (q == 1 || q == 3); - bool fy = (q == 2 || q == 3); - for (int v = 0; v < nVerts; v++) { - int b = v * 6; - dst.push_back(fx ? fw - src[b] : src[b]); - dst.push_back(fy ? fh - src[b + 1] : src[b + 1]); - dst.push_back(src[b + 2]); - dst.push_back(src[b + 3]); - dst.push_back(src[b + 4]); - dst.push_back(src[b + 5]); - } - } - }; - - std::vector expFill, expLine; - expFill.reserve(fillFloats * 4); - expLine.reserve(lineFloats * 4); - expand(m_vertices.data(), m_fillVertexCount, expFill); - expand(m_vertices.data() + fillFloats, m_lineVertexCount, expLine); - - m_vertices.clear(); - m_vertices.insert(m_vertices.end(), expFill.begin(), expFill.end()); - m_vertices.insert(m_vertices.end(), expLine.begin(), expLine.end()); - m_fillVertexCount *= 4; - m_lineVertexCount *= 4; - } - buildCepstrumVertices(w, h); } else { buildVertices(w, h); @@ -434,6 +396,8 @@ void VisualizerWidget::render(QRhiCommandBuffer *cb) { } } + int numPasses = m_mirrored ? 4 : 1; + // Prepare resource updates QRhiResourceUpdateBatch *u = m_rhi->nextResourceUpdateBatch(); @@ -454,13 +418,43 @@ void VisualizerWidget::render(QRhiCommandBuffer *cb) { } } - // Upload single full-screen ortho MVP (slot 0) + // Upload MVP matrices QMatrix4x4 correction = m_rhi->clipSpaceCorrMatrix(); - { + + for (int i = 0; i < numPasses; i++) { QMatrix4x4 proj; proj.ortho(0, (float)w, (float)h, 0, -1, 1); + + if (m_mirrored) { + switch (i) { + case 0: break; + case 1: + proj.translate(w, 0, 0); + proj.scale(-1, 1, 1); + break; + case 2: + proj.translate(0, h, 0); + proj.scale(1, -1, 1); + break; + case 3: + proj.translate(w, h, 0); + proj.scale(-1, -1, 1); + break; + } + } + QMatrix4x4 mvp = correction * proj; - u->updateDynamicBuffer(m_ubuf.get(), 0, 64, mvp.constData()); + u->updateDynamicBuffer(m_ubuf.get(), i * m_ubufAlign, 64, + mvp.constData()); + } + + // Upload full-screen ortho MVP for cepstrum (slot 4) + if (m_mirrored && m_cepstrumVertexCount > 0) { + QMatrix4x4 cepProj; + cepProj.ortho(0, (float)w, (float)h, 0, -1, 1); + QMatrix4x4 cepMvp = correction * cepProj; + u->updateDynamicBuffer(m_ubuf.get(), 4 * m_ubufAlign, 64, + cepMvp.constData()); } // Begin render pass @@ -469,26 +463,30 @@ void VisualizerWidget::render(QRhiCommandBuffer *cb) { (float)outputSize.height()}); const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); - QRhiCommandBuffer::DynamicOffset dynOfs(0, 0); - if (m_fillVertexCount > 0) { - cb->setGraphicsPipeline(m_fillPipeline.get()); - cb->setShaderResources(m_srb.get(), 1, &dynOfs); - cb->setVertexInput(0, 1, &vbufBinding); - cb->draw(m_fillVertexCount); + for (int i = 0; i < numPasses; i++) { + QRhiCommandBuffer::DynamicOffset dynOfs(0, quint32(i * m_ubufAlign)); + + if (m_fillVertexCount > 0) { + cb->setGraphicsPipeline(m_fillPipeline.get()); + cb->setShaderResources(m_srb.get(), 1, &dynOfs); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(m_fillVertexCount); + } + + if (m_lineVertexCount > 0) { + cb->setGraphicsPipeline(m_linePipeline.get()); + cb->setShaderResources(m_srb.get(), 1, &dynOfs); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(m_lineVertexCount, 1, m_fillVertexCount, 0); + } } - if (m_lineVertexCount > 0) { - cb->setGraphicsPipeline(m_linePipeline.get()); - cb->setShaderResources(m_srb.get(), 1, &dynOfs); - cb->setVertexInput(0, 1, &vbufBinding); - cb->draw(m_lineVertexCount, 1, m_fillVertexCount, 0); - } - - // --- Cepstral Thread --- + // --- Cepstral Thread (single full-screen pass, after mirror loop) --- if (m_mirrored && m_cepstrumVertexCount > 0) { + QRhiCommandBuffer::DynamicOffset cepOfs(0, quint32(4 * m_ubufAlign)); cb->setGraphicsPipeline(m_linePipeline.get()); - cb->setShaderResources(m_srb.get(), 1, &dynOfs); + cb->setShaderResources(m_srb.get(), 1, &cepOfs); cb->setVertexInput(0, 1, &vbufBinding); cb->draw(m_cepstrumVertexCount, 1, m_fillVertexCount + m_lineVertexCount, 0); } @@ -679,28 +677,32 @@ void VisualizerWidget::buildVertices(int w, int h) { float fr = fillColor.redF(), fg = fillColor.greenF(), fb = fillColor.blueF(), fa = fillColor.alphaF(); + // In mirrored mode, fade anchor edge to transparent to hide mirror seam + float faAnchor = m_mirrored ? 0.0f : fa; + // Triangle 1 m_vertices.insert(m_vertices.end(), - {x1, anchorY, fr, fg, fb, fa, x1, y1, fr, fg, fb, fa, + {x1, anchorY, fr, fg, fb, faAnchor, x1, y1, fr, fg, fb, fa, x2, y2, fr, fg, fb, fa}); // Triangle 2 m_vertices.insert(m_vertices.end(), - {x1, anchorY, fr, fg, fb, fa, x2, y2, fr, fg, fb, fa, - x2, anchorY, fr, fg, fb, fa}); + {x1, anchorY, fr, fg, fb, faAnchor, x2, y2, fr, fg, fb, fa, + x2, anchorY, fr, fg, fb, faAnchor}); m_fillVertexCount += 6; float lr = lineColor.redF(), lg = lineColor.greenF(), lb = lineColor.blueF(), la = lineColor.alphaF(); + float laAnchor = m_mirrored ? 0.0f : la; // Left edge lineVerts.insert(lineVerts.end(), - {x1, anchorY, lr, lg, lb, la, x1, y1, lr, lg, lb, la}); + {x1, anchorY, lr, lg, lb, laAnchor, x1, y1, lr, lg, lb, la}); // Right edge (last bin only) if (i + 2 == freqs.size()) { lineVerts.insert( lineVerts.end(), - {x2, anchorY, lr, lg, lb, la, x2, y2, lr, lg, lb, la}); + {x2, anchorY, lr, lg, lb, laAnchor, x2, y2, lr, lg, lb, la}); } } }