Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve glsl pre-roll triangles #12100

Merged
merged 14 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,7 @@ else()
if(QOPENGL)
target_sources(mixxx-lib PRIVATE
src/shaders/endoftrackshader.cpp
src/shaders/patternshader.cpp
src/shaders/rgbashader.cpp
src/shaders/rgbshader.cpp
src/shaders/shader.cpp
Expand Down
35 changes: 35 additions & 0 deletions src/shaders/patternshader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "shaders/patternshader.h"

using namespace mixxx;

void PatternShader::init() {
QString vertexShaderCode = QStringLiteral(R"--(
uniform highp mat4 matrix;
attribute highp vec4 position;
Swiftb0y marked this conversation as resolved.
Show resolved Hide resolved
attribute highp vec2 texcoor;
varying highp vec2 vTexcoor;
void main()
{
vTexcoor = texcoor;
gl_Position = matrix * position;
}
)--");

QString fragmentShaderCode = QStringLiteral(R"--(
uniform sampler2D sampler;
Swiftb0y marked this conversation as resolved.
Show resolved Hide resolved
uniform vec2 repetitions;
varying highp vec2 vTexcoor;
void main()
{
gl_FragColor = texture2D(sampler, fract(vTexcoor * repetitions));
}
)--");

load(vertexShaderCode, fragmentShaderCode);

m_matrixLocation = uniformLocation("matrix");
m_samplerLocation = uniformLocation("sampler");
m_repetitionsLocation = uniformLocation("repetitions");
m_positionLocation = attributeLocation("position");
m_texcoordLocation = attributeLocation("texcoor");
}
39 changes: 39 additions & 0 deletions src/shaders/patternshader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include "shaders/shader.h"

namespace mixxx {
class PatternShader;
}

class mixxx::PatternShader final : public mixxx::Shader {
public:
PatternShader() = default;
~PatternShader() = default;
void init();

int matrixLocation() const {
return m_matrixLocation;
}
int samplerLocation() const {
return m_samplerLocation;
}
int repetitionsLocation() const {
return m_repetitionsLocation;
}
int positionLocation() const {
return m_positionLocation;
}
int texcoordLocation() const {
return m_texcoordLocation;
}

private:
int m_matrixLocation;
int m_samplerLocation;
int m_repetitionsLocation;
int m_texcoordLocation;
int m_positionLocation;

DISALLOW_COPY_AND_ASSIGN(PatternShader)
};
10 changes: 5 additions & 5 deletions src/shaders/textureshader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ using namespace mixxx;

void TextureShader::init() {
QString vertexShaderCode = QStringLiteral(R"--(
uniform mat4 matrix;
uniform highp mat4 matrix;
attribute highp vec4 position;
attribute highp vec3 texcoor;
varying highp vec3 vTexcoor;
attribute highp vec2 texcoor;
varying highp vec2 vTexcoor;
void main()
{
vTexcoor = texcoor;
Expand All @@ -17,10 +17,10 @@ void main()

QString fragmentShaderCode = QStringLiteral(R"--(
uniform sampler2D sampler;
varying highp vec3 vTexcoor;
varying highp vec2 vTexcoor;
void main()
{
gl_FragColor = texture2D(sampler, vTexcoor.xy);
gl_FragColor = texture2D(sampler, vTexcoor);
}
)--");

Expand Down
172 changes: 124 additions & 48 deletions src/waveform/renderers/allshader/waveformrendererpreroll.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#include "waveform/renderers/allshader/waveformrendererpreroll.h"

#include <QDomNode>
#include <QOpenGLTexture>
#include <QPainterPath>

#include "skin/legacy/skincontext.h"
#include "track/track.h"
#include "util/texture.h"
#include "waveform/renderers/allshader/matrixforwidgetgeometry.h"
#include "waveform/renderers/waveformwidgetrenderer.h"
#include "widget/wskincolor.h"
Expand All @@ -15,6 +18,8 @@ WaveformRendererPreroll::WaveformRendererPreroll(WaveformWidgetRenderer* wavefor
: WaveformRenderer(waveformWidget) {
}

WaveformRendererPreroll::~WaveformRendererPreroll() = default;

void WaveformRendererPreroll::setup(
const QDomNode& node, const SkinContext& context) {
m_color.setNamedColor(context.selectString(node, "SignalColor"));
Expand All @@ -32,8 +37,6 @@ void WaveformRendererPreroll::paintGL() {
return;
}

m_vertices.clear();

const double firstDisplayedPosition = m_waveformRenderer->getFirstDisplayedPosition();
const double lastDisplayedPosition = m_waveformRenderer->getLastDisplayedPosition();

Expand All @@ -53,45 +56,68 @@ void WaveformRendererPreroll::paintGL() {
const int currentVSamplePosition = m_waveformRenderer->getPlayPosVSample();
const int totalVSamples = m_waveformRenderer->getTotalVSample();

const float halfBreadth = m_waveformRenderer->getBreadth() / 2.0f;
const float halfPolyBreadth = m_waveformRenderer->getBreadth() / 5.0f;
const float markerBreadth = m_waveformRenderer->getBreadth() * 0.4f;

const double polyPixelWidth = 40.0 / vSamplesPerPixel;
const float halfBreadth = m_waveformRenderer->getBreadth() * 0.5f;
const float halfMarkerBreadth = markerBreadth * 0.5f;

const int maxNumTriangles =
static_cast<int>(
static_cast<double>(m_waveformRenderer->getLength()) /
polyPixelWidth) +
2;
const int numVerticesPerTriangle = 3;
const int reserved = maxNumTriangles * numVerticesPerTriangle;
// for most pessimistic case, where we fill the entire display with triangles
m_vertices.reserve(reserved);
const double markerLength = 40.0 / vSamplesPerPixel;

// A series of markers will be drawn (by repeating the texture in a pattern)
// from the left of the screen up until start (preroll) and from the right
// of the screen up until the end (postroll) of the track respectively.

const float epsilon = 0.5f;
if (std::abs(m_markerLength - markerLength) > epsilon ||
std::abs(m_markerBreadth - markerBreadth) > epsilon) {
// Regenerate the texture with the preroll marker (a triangle) if the size
// has changed size last time.
generateTexture(markerLength, markerBreadth);
}

if (!m_pTexture) {
return;
}

const int matrixLocation = m_shader.matrixLocation();
const int samplerLocation = m_shader.samplerLocation();
const int vertexLocation = m_shader.positionLocation();
const int texcoordLocation = m_shader.texcoordLocation();

// Set up the shader
m_shader.bind();

m_shader.enableAttributeArray(vertexLocation);
m_shader.enableAttributeArray(texcoordLocation);

const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false);

m_shader.setUniformValue(matrixLocation, matrix);
m_shader.setUniformValue(samplerLocation, 0);

m_pTexture->bind();

if (preRollVisible) {
// VSample position of the right-most triangle's tip
const double triangleTipVSamplePosition =
numberOfVSamples * playMarkerPosition -
playMarkerPosition * numberOfVSamples -
currentVSamplePosition;
// In pixels
double x = triangleTipVSamplePosition / vSamplesPerPixel;
const double limit =
static_cast<double>(m_waveformRenderer->getLength()) +
polyPixelWidth;
markerLength;
if (x >= limit) {
// Don't draw invisible triangles beyond the right side of the display
x -= std::ceil((x - limit) / polyPixelWidth) * polyPixelWidth;
x -= std::ceil((x - limit) / markerLength) * markerLength;
}

while (x >= 0) {
const float x1 = static_cast<float>(x);
const float x2 = static_cast<float>(x - polyPixelWidth);
m_vertices.addTriangle({x1, halfBreadth},
{x2, halfBreadth - halfPolyBreadth},
{x2, halfBreadth + halfPolyBreadth});

x -= polyPixelWidth;
}
drawPattern(0,
halfBreadth - halfMarkerBreadth,
x,
halfBreadth + halfMarkerBreadth,
x / markerLength,
true);
}

if (postRollVisible) {
Expand All @@ -102,44 +128,94 @@ void WaveformRendererPreroll::paintGL() {
remainingVSamples;
// In pixels
double x = triangleTipVSamplePosition / vSamplesPerPixel;
const double limit = -polyPixelWidth;
const double limit = -markerLength;
if (x <= limit) {
// Don't draw invisible triangles before the left side of the display
x += std::ceil((limit - x) / polyPixelWidth) * polyPixelWidth;
x += std::ceil((limit - x) / markerLength) * markerLength;
}

const double end = static_cast<double>(m_waveformRenderer->getLength());
while (x < end) {
const float x1 = static_cast<float>(x);
const float x2 = static_cast<float>(x + polyPixelWidth);
m_vertices.addTriangle({x1, halfBreadth},
{x2, halfBreadth - halfPolyBreadth},
{x2, halfBreadth + halfPolyBreadth});
x += polyPixelWidth;
}
drawPattern(x,
halfBreadth - halfMarkerBreadth,
end,
halfBreadth + halfMarkerBreadth,
(end - x) / markerLength,
false);
}

DEBUG_ASSERT(m_vertices.size() <= reserved);
m_pTexture->release();

const int vertexLocation = m_shader.attributeLocation("position");
const int matrixLocation = m_shader.uniformLocation("matrix");
const int colorLocation = m_shader.uniformLocation("color");
m_shader.disableAttributeArray(vertexLocation);
m_shader.disableAttributeArray(texcoordLocation);
m_shader.release();
}

m_shader.bind();
m_shader.enableAttributeArray(vertexLocation);
void WaveformRendererPreroll::drawPattern(
float x1, float y1, float x2, float y2, double repetitions, bool flip) {
// Draw a large rectangle with a repeating pattern of the texture
const int numVerticesPerTriangle = 3;
const int reserved = 2 * numVerticesPerTriangle;
const int repetitionsLocation = m_shader.repetitionsLocation();
const int vertexLocation = m_shader.positionLocation();
const int texcoordLocation = m_shader.texcoordLocation();

const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false);
m_vertices.clear();
m_texcoords.clear();
m_vertices.reserve(reserved);
m_texcoords.reserve(reserved);
m_vertices.addRectangle(x1, y1, x2, y2);
m_texcoords.addRectangle(flip ? 1.f : 0.f, 0.f, flip ? 0.f : 1.f, 1.f);

m_shader.setUniformValue(repetitionsLocation, QVector2D(repetitions, 1.0));

m_shader.setAttributeArray(
vertexLocation, GL_FLOAT, m_vertices.constData(), 2);

m_shader.setUniformValue(matrixLocation, matrix);
m_shader.setUniformValue(colorLocation, m_color);
m_shader.setAttributeArray(
texcoordLocation, GL_FLOAT, m_texcoords.constData(), 2);

glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
}

m_shader.disableAttributeArray(vertexLocation);
m_shader.release();
void WaveformRendererPreroll::generateTexture(float markerLength, float markerBreadth) {
const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio();
m_markerLength = markerLength;
m_markerBreadth = markerBreadth;
Swiftb0y marked this conversation as resolved.
Show resolved Hide resolved

const int imagePixelW = static_cast<int>(m_markerLength * devicePixelRatio + 0.5f);
const int imagePixelH = static_cast<int>(m_markerBreadth * devicePixelRatio + 0.5f);
const float imageW = static_cast<float>(imagePixelW) / devicePixelRatio;
const float imageH = static_cast<float>(imagePixelH) / devicePixelRatio;

QImage image(imagePixelW, imagePixelH, QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(devicePixelRatio);

const float penWidth = 1.5f;
const float offset = penWidth / 2.f;

image.fill(QColor(0, 0, 0, 0).rgba());
Swiftb0y marked this conversation as resolved.
Show resolved Hide resolved
QPainter painter;
painter.begin(&image);

painter.setWorldMatrixEnabled(false);

QPen pen(m_color);
pen.setWidthF(penWidth);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.setRenderHints(QPainter::Antialiasing);
// Draw base the right, tip to the left
QPointF p0{imageW - offset, offset};
QPointF p1{imageW - offset, imageH - offset};
QPointF p2{offset, imageH / 2.f};
QPainterPath path;
path.moveTo(p2);
path.lineTo(p1);
path.lineTo(p0);
path.closeSubpath();
painter.drawPath(path);
painter.end();

m_pTexture.reset(createTexture(image));
}

} // namespace allshader
14 changes: 12 additions & 2 deletions src/waveform/renderers/allshader/waveformrendererpreroll.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#pragma once

#include <QColor>
#include <QImage>

#include "shaders/unicolorshader.h"
#include "shaders/patternshader.h"
#include "util/class.h"
#include "waveform/renderers/allshader/vertexdata.h"
#include "waveform/renderers/allshader/waveformrenderer.h"

class QDomNode;
class SkinContext;
class QOpenGLTexture;

namespace allshader {
class WaveformRendererPreroll;
Expand All @@ -17,15 +19,23 @@ class WaveformRendererPreroll;
class allshader::WaveformRendererPreroll final : public allshader::WaveformRenderer {
public:
explicit WaveformRendererPreroll(WaveformWidgetRenderer* waveformWidgetRenderer);
~WaveformRendererPreroll() override;

void setup(const QDomNode& node, const SkinContext& context) override;
void paintGL() override;
void initializeGL() override;

private:
mixxx::UnicolorShader m_shader;
void drawPattern(float x1, float y1, float x2, float y2, double repetitions, bool flip);
void generateTexture(float markerLength, float markerBreadth);

mixxx::PatternShader m_shader;
QColor m_color;
float m_markerBreadth{};
float m_markerLength{};
std::unique_ptr<QOpenGLTexture> m_pTexture;
VertexData m_vertices;
VertexData m_texcoords;
Swiftb0y marked this conversation as resolved.
Show resolved Hide resolved

DISALLOW_COPY_AND_ASSIGN(WaveformRendererPreroll);
};
Loading