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

PR that makes UI with QOpenGL waveform responsive on Windows #4

Closed
wants to merge 8 commits into from
42 changes: 39 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ if(NOT CMAKE_CONFIGURATION_TYPES)
endif()

option(QT6 "Build with Qt6" OFF)
if(APPLE)
option(QOPENGL "Use QOpenGL... classes instead of QGLWidget" ON)
else()
option(QOPENGL "Use QOpenGL... classes instead of QGLWidget" OFF)
endif()

if(QOPENGL)
add_compile_definitions(MIXXX_USE_QOPENGL)
endif()

if(APPLE)
if(QT6)
Expand Down Expand Up @@ -1113,12 +1122,12 @@ else()
src/waveform/renderers/waveformrendermarkrange.cpp
src/waveform/renderers/waveformsignalcolors.cpp
src/waveform/renderers/waveformwidgetrenderer.cpp
src/waveform/sharedglcontext.cpp
src/waveform/visualsmanager.cpp
src/waveform/vsyncthread.cpp
src/waveform/waveformmarklabel.cpp
src/waveform/waveformwidgetfactory.cpp
src/waveform/widgets/emptywaveformwidget.cpp
src/waveform/widgets/glwaveformwidgetabstract.cpp
src/waveform/widgets/glrgbwaveformwidget.cpp
src/waveform/widgets/glsimplewaveformwidget.cpp
src/waveform/widgets/glslwaveformwidget.cpp
Expand All @@ -1137,11 +1146,38 @@ else()
src/widget/woverviewhsv.cpp
src/widget/woverviewlmh.cpp
src/widget/woverviewrgb.cpp
src/widget/wspinny.cpp
src/widget/wvumetergl.cpp
src/widget/wwaveformviewer.cpp
)
if(QOPENGL)
target_sources(mixxx-lib PRIVATE
src/widget/openglwindow.cpp
src/widget/wglwidgetqopengl.cpp
src/widget/qopengl/wvumetergl.cpp
src/widget/qopengl/wspinny.cpp
src/waveform/renderers/qopengl/waveformrenderbackground.cpp
src/waveform/renderers/qopengl/waveformrenderbeat.cpp
src/waveform/renderers/qopengl/waveformrenderer.cpp
src/waveform/renderers/qopengl/waveformrendererendoftrack.cpp
src/waveform/renderers/qopengl/waveformrendererpreroll.cpp
src/waveform/renderers/qopengl/waveformrendererrgb.cpp
src/waveform/renderers/qopengl/waveformrenderersignalbase.cpp
src/waveform/renderers/qopengl/waveformrendermark.cpp
src/waveform/renderers/qopengl/waveformrendermarkrange.cpp
src/waveform/widgets/qopengl/waveformwidget.cpp
src/waveform/widgets/qopengl/rgbwaveformwidget.cpp
src/waveform/widgets/qopengl/waveformwidget.cpp
src/waveform/widgets/qopengl/rgbwaveformwidget.cpp
)
else()
target_sources(mixxx-lib PRIVATE
src/waveform/sharedglcontext.cpp
src/widget/wglwidgetqglwidget.cpp
src/widget/wvumetergl.cpp
src/widget/wspinny.cpp
)
endif()
endif()

set_target_properties(mixxx-lib PROPERTIES AUTOMOC ON AUTOUIC ON CXX_CLANG_TIDY "${CLANG_TIDY}")
target_include_directories(mixxx-lib PUBLIC src "${CMAKE_CURRENT_BINARY_DIR}/src")
if(UNIX AND NOT APPLE)
Expand Down
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ int main(int argc, char * argv[]) {
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#ifdef MIXXX_USE_QOPENGL
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
#endif

// workaround for https://bugreports.qt.io/browse/QTBUG-84363
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(5, 15, 1)
Expand Down
6 changes: 6 additions & 0 deletions src/mixxxmainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

#include <QDesktopServices>
#include <QFileDialog>

#ifndef MIXXX_USE_QOPENGL
#include <QGLFormat>
#endif

#include <QUrl>
#include <QtDebug>

Expand Down Expand Up @@ -152,6 +156,7 @@ void MixxxMainWindow::initialize() {
}
});

#ifndef MIXXX_USE_QOPENGL
// Before creating the first skin we need to create a QGLWidget so that all
// the QGLWidget's we create can use it as a shared QGLContext.
if (!CmdlineArgs::Instance().getSafeMode() && QGLFormat::hasOpenGL()) {
Expand Down Expand Up @@ -181,6 +186,7 @@ void MixxxMainWindow::initialize() {
pContextWidget->hide();
SharedGLContext::setWidget(pContextWidget);
}
#endif

WaveformWidgetFactory::createInstance(); // takes a long time
WaveformWidgetFactory::instance()->setConfig(m_pCoreServices->getSettings());
Expand Down
11 changes: 8 additions & 3 deletions src/skin/legacy/legacyskinparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1314,8 +1314,13 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) {
connect(waveformWidgetFactory,
&WaveformWidgetFactory::renderSpinnies,
pSpinny,
&WSpinny::render);
connect(waveformWidgetFactory, &WaveformWidgetFactory::swapSpinnies, pSpinny, &WSpinny::swap);
&WSpinny::render,
Qt::DirectConnection);
connect(waveformWidgetFactory,
&WaveformWidgetFactory::swapSpinnies,
pSpinny,
&WSpinny::swap,
Qt::DirectConnection);
connect(pSpinny,
&WSpinny::trackDropped,
m_pPlayerManager,
Expand Down Expand Up @@ -1348,7 +1353,7 @@ QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) {
return pVuMeterWidget;
}

// QGLWidget derived WVuMeterGL
// WGLWidget derived WVuMeterGL

if (CmdlineArgs::Instance().getSafeMode()) {
WLabel* dummy = new WLabel(m_pParent);
Expand Down
4 changes: 4 additions & 0 deletions src/util/widgetrendertimer.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "util/widgetrendertimer.h"

#ifdef USE_WIDGET_RENDER_TIMER

#include "moc_widgetrendertimer.cpp"
#include "util/time.h"

Expand Down Expand Up @@ -28,3 +30,5 @@ void WidgetRenderTimer::activity() {
m_guiTickTimer.start(m_renderFrequency);
}
}

#endif
11 changes: 11 additions & 0 deletions src/util/widgetrendertimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
// Ironically, using this class somehow causes severe lagginess on mouse input
// with Windows, so use #ifdefs to only call activity() on macOS; just call
// QWidget::update() for other operating systems.
//
// Also when using the QOpenGLWindow based WGLWidget (when MIXXX_USE_QOPENGL is
// defined) using this seems not necessary and makes causes lagginess
#ifdef __APPLE__
#ifndef MIXXX_USE_QOPENGL
#define USE_WIDGET_RENDER_TIMER
#endif
#endif

#ifdef USE_WIDGET_RENDER_TIMER
class WidgetRenderTimer : public QObject {
Q_OBJECT
public:
Expand Down Expand Up @@ -53,3 +63,4 @@ class WidgetRenderTimer : public QObject {
mixxx::Duration m_lastActivity;
mixxx::Duration m_lastRender;
};
#endif
17 changes: 12 additions & 5 deletions src/waveform/renderers/glslwaveformrenderersignal.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#include "waveform/renderers/glslwaveformrenderersignal.h"
#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2)

#ifdef MIXXX_USE_QOPENGL
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>
#else
#endif
#include <QGLFramebufferObject>
#include <QGLShaderProgram>

Expand Down Expand Up @@ -49,14 +54,16 @@ bool GLSLWaveformRendererSignal::loadShaders() {
m_frameShaderProgram->removeAllShaders();

if (!m_frameShaderProgram->addShaderFromSourceFile(
QGLShader::Vertex, ":/shaders/passthrough.vert")) {
GL_SHADER_CLASS::Vertex,
":/shaders/passthrough.vert")) {
qDebug() << "GLWaveformRendererSignalShader::loadShaders - "
<< m_frameShaderProgram->log();
return false;
}

if (!m_frameShaderProgram->addShaderFromSourceFile(
QGLShader::Fragment, m_pFragShader)) {
GL_SHADER_CLASS::Fragment,
m_pFragShader)) {
qDebug() << "GLWaveformRendererSignalShader::loadShaders - "
<< m_frameShaderProgram->log();
return false;
Expand Down Expand Up @@ -182,8 +189,8 @@ void GLSLWaveformRendererSignal::createFrameBuffers() {
static_cast<int>(
m_waveformRenderer->getHeight() * devicePixelRatio);

m_framebuffer = std::make_unique<QGLFramebufferObject>(bufferWidth,
bufferHeight);
m_framebuffer = std::make_unique<GL_FBO_CLASS>(bufferWidth,
bufferHeight);

if (!m_framebuffer->isValid()) {
qWarning() << "GLSLWaveformRendererSignal::createFrameBuffer - frame buffer not valid";
Expand All @@ -195,7 +202,7 @@ void GLSLWaveformRendererSignal::onInitializeGL() {
m_textureRenderedWaveformCompletion = 0;

if (!m_frameShaderProgram) {
m_frameShaderProgram = std::make_unique<QGLShaderProgram>();
m_frameShaderProgram = std::make_unique<GL_SHADER_PROGRAM_CLASS>();
}

if (!loadShaders()) {
Expand Down
18 changes: 14 additions & 4 deletions src/waveform/renderers/glslwaveformrenderersignal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@
#include "util/memory.h"
#include "waveform/renderers/glwaveformrenderer.h"

QT_FORWARD_DECLARE_CLASS(QGLFramebufferObject)
QT_FORWARD_DECLARE_CLASS(QGLShaderProgram)
#ifdef MIXXX_USE_QOPENGL
#define GL_FBO_CLASS QOpenGLFramebufferObject
#define GL_SHADER_CLASS QOpenGLShader
#define GL_SHADER_PROGRAM_CLASS QOpenGLShaderProgram
#else
#define GL_FBO_CLASS QGLFramebufferObject
#define GL_SHADER_CLASS QGLShader
#define GL_SHADER_PROGRAM_CLASS QGLShaderProgram
#endif

QT_FORWARD_DECLARE_CLASS(GL_FBO_CLASS)
QT_FORWARD_DECLARE_CLASS(GL_SHADER_PROGRAM_CLASS)

class GLSLWaveformRendererSignal : public QObject,
public GLWaveformRenderer {
Expand Down Expand Up @@ -50,15 +60,15 @@ class GLSLWaveformRendererSignal : public QObject,
int m_textureRenderedWaveformCompletion;

// Frame buffer for two pass rendering.
std::unique_ptr<QGLFramebufferObject> m_framebuffer;
std::unique_ptr<GL_FBO_CLASS> m_framebuffer;

bool m_bDumpPng;

// shaders
bool m_shadersValid;
ColorType m_colorType;
const QString m_pFragShader;
std::unique_ptr<QGLShaderProgram> m_frameShaderProgram;
std::unique_ptr<GL_SHADER_PROGRAM_CLASS> m_frameShaderProgram;
};

class GLSLWaveformRendererFilteredSignal: public GLSLWaveformRendererSignal {
Expand Down
14 changes: 12 additions & 2 deletions src/waveform/renderers/glwaveformrenderer.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#ifndef MIXXX_USE_QOPENGL
#include <QGLContext>
#endif
#include <QOpenGLFunctions_2_1>

#include "waveform/renderers/waveformrenderersignalbase.h"
Expand All @@ -15,8 +17,12 @@
class GLWaveformRenderer : public WaveformRendererSignalBase, protected QOpenGLFunctions_2_1 {
public:
GLWaveformRenderer(WaveformWidgetRenderer* waveformWidgetRenderer)
: WaveformRendererSignalBase(waveformWidgetRenderer),
m_pLastContext(nullptr) {
: WaveformRendererSignalBase(waveformWidgetRenderer)
#ifndef MIXXX_USE_QOPENGL
,
m_pLastContext(nullptr)
#endif
{
}

virtual void onInitializeGL() {
Expand All @@ -28,14 +34,18 @@ class GLWaveformRenderer : public WaveformRendererSignalBase, protected QOpenGLF
// by calling this in `draw` when the QGLContext has been made current.
// TODO: remove this when upgrading to QOpenGLWidget
void maybeInitializeGL() {
#ifndef MIXXX_USE_QOPENGL
if (QGLContext::currentContext() != m_pLastContext) {
onInitializeGL();
m_pLastContext = QGLContext::currentContext();
}
#endif
}

private:
#ifndef MIXXX_USE_QOPENGL
const QGLContext* m_pLastContext;
#endif
};

#endif // !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2)
64 changes: 64 additions & 0 deletions src/waveform/renderers/qopengl/fixedpointcalc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// functions to do fixed point calculations, used by qopengl::WaveformRendererRGB

#include <cmath>

// float to fixed point with 8 fractional bits, clipped at 4.0
inline uint32_t toFrac8(float x) {
return std::min<uint32_t>(static_cast<uint32_t>(std::max(x, 0.f) * 256.f), 4 * 256);
}

// scaled sqrt lookable table to convert maxAll and maxAllNext as calculated
// in updatePaintNode back to y coordinates
class Frac16SqrtTableSingleton {
public:
static constexpr size_t frac16sqrtTableSize{(3 * 4 * 255 * 256) / 16 + 1};

static Frac16SqrtTableSingleton& getInstance() {
static Frac16SqrtTableSingleton instance;
return instance;
}

inline float get(uint32_t x) const {
// The maximum value of fact16x can be (as uint32_t) 3 * 4 * 255 * 256,
// which would be exessive for the table size. We divide by 16 in order
// to get a more reasonable size.
return m_table[x >> 4];
}

private:
float* m_table;
Frac16SqrtTableSingleton()
: m_table(new float[frac16sqrtTableSize]) {
// In the original implementation, the result of sqrt(maxAll) is divided
// by sqrt(3 * 255 * 255);
// We get rid of that division and bake it into this table.
// Additionally, we divide the index for the lookup by 16 (see get(...)),
// so we need to invert that here.
const float f = (3.f * 255.f * 255.f / 16.f);
for (uint32_t i = 0; i < frac16sqrtTableSize; i++) {
m_table[i] = std::sqrt(static_cast<float>(i) / f);
}
}
~Frac16SqrtTableSingleton() {
delete[] m_table;
}
Frac16SqrtTableSingleton(const Frac16SqrtTableSingleton&) = delete;
Frac16SqrtTableSingleton& operator=(const Frac16SqrtTableSingleton&) = delete;
};

inline float frac16_sqrt(uint32_t x) {
return Frac16SqrtTableSingleton::getInstance().get(x);
}

inline uint32_t frac8Pow2ToFrac16(uint32_t x) {
// x is the result of multiplying two fixedpoint values with 8 fraction bits,
// thus x has 16 fraction bits, which is also what we want to return for this
// function. We would naively return (x * x) >> 16, but x * x would overflow
// the 32 bits for values > 1, so we shift before multiplying.
x >>= 8;
return (x * x);
}

inline uint32_t math_max_u32(uint32_t a, uint32_t b, uint32_t c) {
return std::max(a, std::max(b, c));
}
18 changes: 18 additions & 0 deletions src/waveform/renderers/qopengl/iwaveformrenderer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <QOpenGLFunctions>

/// Interface for QOpenGL-based waveform renderers

namespace qopengl {
class IWaveformRenderer;
}

class qopengl::IWaveformRenderer : public QOpenGLFunctions {
public:
virtual void initializeGL() {
}
virtual void resizeGL(int w, int h) {
}
virtual void renderGL() = 0;
};
Loading