diff --git a/CMakeLists.txt b/CMakeLists.txt index 7accbdb8d90..9d1b74a06e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -1113,7 +1122,6 @@ 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 @@ -1138,10 +1146,22 @@ else() src/widget/woverviewlmh.cpp src/widget/woverviewrgb.cpp src/widget/wspinny.cpp - src/widget/wvumetergl.cpp + src/widget/wvumetergl.cpp src/widget/wwaveformviewer.cpp ) + if(QOPENGL) + target_sources(mixxx-lib PRIVATE + src/widget/openglwindow.cpp + src/widget/wglwidgetqopengl.cpp + ) + else() + target_sources(mixxx-lib PRIVATE + src/waveform/sharedglcontext.cpp + src/widget/wglwidgetqglwidget.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) diff --git a/src/main.cpp b/src/main.cpp index 6bb4574d066..a28350e6d19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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) diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index b5a6b8e3fe4..15b1319bab3 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -2,7 +2,11 @@ #include #include + +#ifndef MIXXX_USE_QOPENGL #include +#endif + #include #include @@ -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()) { @@ -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()); diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 5698670dd2a..ffa17cb4e96 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -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, @@ -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); diff --git a/src/util/widgetrendertimer.cpp b/src/util/widgetrendertimer.cpp index 43b51a40f0a..1b102de6068 100644 --- a/src/util/widgetrendertimer.cpp +++ b/src/util/widgetrendertimer.cpp @@ -1,5 +1,7 @@ #include "util/widgetrendertimer.h" +#ifdef USE_WIDGET_RENDER_TIMER + #include "moc_widgetrendertimer.cpp" #include "util/time.h" @@ -28,3 +30,5 @@ void WidgetRenderTimer::activity() { m_guiTickTimer.start(m_renderFrequency); } } + +#endif diff --git a/src/util/widgetrendertimer.h b/src/util/widgetrendertimer.h index 25fc2f3cc8a..7fec8ccedd5 100644 --- a/src/util/widgetrendertimer.h +++ b/src/util/widgetrendertimer.h @@ -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: @@ -53,3 +63,4 @@ class WidgetRenderTimer : public QObject { mixxx::Duration m_lastActivity; mixxx::Duration m_lastRender; }; +#endif diff --git a/src/waveform/renderers/glslwaveformrenderersignal.cpp b/src/waveform/renderers/glslwaveformrenderersignal.cpp index 5dc12bf91f2..2177e910a42 100644 --- a/src/waveform/renderers/glslwaveformrenderersignal.cpp +++ b/src/waveform/renderers/glslwaveformrenderersignal.cpp @@ -1,6 +1,11 @@ #include "waveform/renderers/glslwaveformrenderersignal.h" #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) +#ifdef MIXXX_USE_QOPENGL +#include +#include +#else +#endif #include #include @@ -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; @@ -182,8 +189,8 @@ void GLSLWaveformRendererSignal::createFrameBuffers() { static_cast( m_waveformRenderer->getHeight() * devicePixelRatio); - m_framebuffer = std::make_unique(bufferWidth, - bufferHeight); + m_framebuffer = std::make_unique(bufferWidth, + bufferHeight); if (!m_framebuffer->isValid()) { qWarning() << "GLSLWaveformRendererSignal::createFrameBuffer - frame buffer not valid"; @@ -195,7 +202,7 @@ void GLSLWaveformRendererSignal::onInitializeGL() { m_textureRenderedWaveformCompletion = 0; if (!m_frameShaderProgram) { - m_frameShaderProgram = std::make_unique(); + m_frameShaderProgram = std::make_unique(); } if (!loadShaders()) { diff --git a/src/waveform/renderers/glslwaveformrenderersignal.h b/src/waveform/renderers/glslwaveformrenderersignal.h index 4243732ea93..e6da6b0cacf 100644 --- a/src/waveform/renderers/glslwaveformrenderersignal.h +++ b/src/waveform/renderers/glslwaveformrenderersignal.h @@ -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 { @@ -50,7 +60,7 @@ class GLSLWaveformRendererSignal : public QObject, int m_textureRenderedWaveformCompletion; // Frame buffer for two pass rendering. - std::unique_ptr m_framebuffer; + std::unique_ptr m_framebuffer; bool m_bDumpPng; @@ -58,7 +68,7 @@ class GLSLWaveformRendererSignal : public QObject, bool m_shadersValid; ColorType m_colorType; const QString m_pFragShader; - std::unique_ptr m_frameShaderProgram; + std::unique_ptr m_frameShaderProgram; }; class GLSLWaveformRendererFilteredSignal: public GLSLWaveformRendererSignal { diff --git a/src/waveform/renderers/glwaveformrenderer.h b/src/waveform/renderers/glwaveformrenderer.h index 61c05ea6297..b228ae82b22 100644 --- a/src/waveform/renderers/glwaveformrenderer.h +++ b/src/waveform/renderers/glwaveformrenderer.h @@ -1,6 +1,8 @@ #pragma once +#ifndef MIXXX_USE_QOPENGL #include +#endif #include #include "waveform/renderers/waveformrenderersignalbase.h" @@ -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() { @@ -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) diff --git a/src/waveform/renderers/waveformrenderbeat.cpp b/src/waveform/renderers/waveformrenderbeat.cpp index b4bc76ab906..09f78b665a2 100644 --- a/src/waveform/renderers/waveformrenderbeat.cpp +++ b/src/waveform/renderers/waveformrenderbeat.cpp @@ -40,7 +40,12 @@ void WaveformRenderBeat::draw(QPainter* painter, QPaintEvent* /*event*/) { if (alpha == 0) { return; } +#ifdef MIXXX_USE_QOPENGL + // TODO @m0dB using alpha transparency for the beat lines causes has artifacts with QOpenGL. + m_beatColor.setAlphaF(1.f); +#else m_beatColor.setAlphaF(alpha/100.0); +#endif const int trackSamples = m_waveformRenderer->getTrackSamples(); if (trackSamples <= 0) { diff --git a/src/waveform/vsyncthread.cpp b/src/waveform/vsyncthread.cpp index 4ec2e571c8d..5b196eb80de 100644 --- a/src/waveform/vsyncthread.cpp +++ b/src/waveform/vsyncthread.cpp @@ -1,6 +1,5 @@ #include "vsyncthread.h" -#include #include #include #include diff --git a/src/waveform/vsyncthread.h b/src/waveform/vsyncthread.h index f96259223a5..05d07ab4a62 100644 --- a/src/waveform/vsyncthread.h +++ b/src/waveform/vsyncthread.h @@ -1,12 +1,12 @@ #pragma once -#include -#include -#include #include -#include +#include +#include +#include #include "util/performancetimer.h" +#include "widget/wglwidget.h" class VSyncThread : public QThread { Q_OBJECT @@ -25,7 +25,7 @@ class VSyncThread : public QThread { void run(); - bool waitForVideoSync(QGLWidget* glw); + bool waitForVideoSync(WGLWidget* glw); int elapsed(); int toNextSyncMicros(); void setSyncIntervalTimeMicros(int usSyncTimer); @@ -35,8 +35,8 @@ class VSyncThread : public QThread { int fromTimerToNextSyncMicros(const PerformanceTimer& timer); void vsyncSlotFinished(); void getAvailableVSyncTypes(QList>* list); - void setupSync(QGLWidget* glw, int index); - void waitUntilSwap(QGLWidget* glw); + void setupSync(WGLWidget* glw, int index); + void waitUntilSwap(WGLWidget* glw); mixxx::Duration sinceLastSwap() const; signals: void vsyncRender(); diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 1c60114418c..8f9c7804962 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -1,7 +1,10 @@ #include "waveform/waveformwidgetfactory.h" +#ifndef MIXXX_USE_QOPENGL #include #include +#endif + #include #include #include @@ -48,25 +51,14 @@ bool shouldRenderWaveform(WaveformWidgetAbstract* pWaveformWidget) { return false; } - auto* glw = qobject_cast(pWaveformWidget->getWidget()); + auto* glw = pWaveformWidget->getGLWidget(); if (glw == nullptr) { - // Not a QGLWidget. We can simply use QWidget::isVisible. + // Not a WGLWidget. We can simply use QWidget::isVisible. auto* qwidget = qobject_cast(pWaveformWidget->getWidget()); return qwidget != nullptr && qwidget->isVisible(); } - if (glw == nullptr || !glw->isValid() || !glw->isVisible()) { - return false; - } - - // Strangely, a widget can have non-zero width/height, be valid and visible, - // yet still not show up on the screen. QWindow::isExposed tells us this. - const QWindow* window = glw->windowHandle(); - if (window == nullptr || !window->isExposed()) { - return false; - } - - return true; + return glw->shouldRender(); } } // anonymous namespace @@ -125,6 +117,12 @@ WaveformWidgetFactory::WaveformWidgetFactory() m_visualGain[Mid] = 1.0; m_visualGain[High] = 1.0; +#ifdef MIXXX_USE_QOPENGL + // TODO @m0dB We might want to check, but as this is intended for macOS + // we can be sure the OpenGL is available + m_openGlAvailable = true; + m_openGLShaderAvailable = true; +#else QGLWidget* pGlWidget = SharedGLContext::getWidget(); if (pGlWidget && pGlWidget->isValid()) { // will be false if SafeMode is enabled @@ -265,7 +263,7 @@ WaveformWidgetFactory::WaveformWidgetFactory() pGlWidget->hide(); } - +#endif evaluateWidgets(); m_time.start(); } @@ -370,11 +368,13 @@ void WaveformWidgetFactory::addVuMeter(WVuMeterGL* pVuMeter) { connect(this, &WaveformWidgetFactory::renderVuMeters, pVuMeter, - &WVuMeterGL::render); + &WVuMeterGL::render, + Qt::DirectConnection); connect(this, &WaveformWidgetFactory::swapVuMeters, pVuMeter, - &WVuMeterGL::swap); + &WVuMeterGL::swap, + Qt::DirectConnection); } void WaveformWidgetFactory::slotSkinLoaded() { @@ -683,7 +683,7 @@ void WaveformWidgetFactory::render() { } } - // WSpinnys are also double-buffered QGLWidgets, like all the waveform + // WSpinnys are also double-buffered WGLWidgets, like all the waveform // renderers. Render all the WSpinny widgets now. emit renderSpinnies(m_vsyncThread); // Same for WVuMeterGL. Note that we are either using WVuMeter or WVuMeterGL. @@ -732,11 +732,9 @@ void WaveformWidgetFactory::swap() { if (!shouldRenderWaveform(pWaveformWidget)) { continue; } - QGLWidget* glw = qobject_cast(pWaveformWidget->getWidget()); + WGLWidget* glw = pWaveformWidget->getGLWidget(); if (glw != nullptr) { - if (glw->context() != QGLContext::currentContext()) { - glw->makeCurrent(); - } + glw->makeCurrentIfNeeded(); glw->swapBuffers(); } //qDebug() << "swap x" << m_vsyncThread->elapsed(); diff --git a/src/waveform/widgets/glrgbwaveformwidget.cpp b/src/waveform/widgets/glrgbwaveformwidget.cpp index 1e41073f6e6..260ac4118c3 100644 --- a/src/waveform/widgets/glrgbwaveformwidget.cpp +++ b/src/waveform/widgets/glrgbwaveformwidget.cpp @@ -14,9 +14,9 @@ GLRGBWaveformWidget::GLRGBWaveformWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); addRenderer(); @@ -28,11 +28,6 @@ GLRGBWaveformWidget::GLRGBWaveformWidget(const QString& group, QWidget* parent) addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -55,7 +50,7 @@ mixxx::Duration GLRGBWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/glsimplewaveformwidget.cpp b/src/waveform/widgets/glsimplewaveformwidget.cpp index 3213640c98f..5b9166a94cc 100644 --- a/src/waveform/widgets/glsimplewaveformwidget.cpp +++ b/src/waveform/widgets/glsimplewaveformwidget.cpp @@ -17,9 +17,9 @@ GLSimpleWaveformWidget::GLSimpleWaveformWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); addRenderer(); @@ -31,11 +31,6 @@ GLSimpleWaveformWidget::GLSimpleWaveformWidget(const QString& group, QWidget* pa addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -57,7 +52,7 @@ mixxx::Duration GLSimpleWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/glslwaveformwidget.cpp b/src/waveform/widgets/glslwaveformwidget.cpp index 7cfb66f42b8..16c74884f53 100644 --- a/src/waveform/widgets/glslwaveformwidget.cpp +++ b/src/waveform/widgets/glslwaveformwidget.cpp @@ -38,12 +38,11 @@ GLSLWaveformWidget::GLSLWaveformWidget( QWidget* parent, GlslType type) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); - if (QGLContext::currentContext() != context()) { - makeCurrent(); - } + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); + + makeCurrentIfNeeded(); addRenderer(); addRenderer(); @@ -63,11 +62,6 @@ GLSLWaveformWidget::GLSLWaveformWidget( addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -86,7 +80,7 @@ mixxx::Duration GLSLWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); @@ -97,13 +91,13 @@ mixxx::Duration GLSLWaveformWidget::render() { void GLSLWaveformWidget::resize(int width, int height) { // NOTE: (vrince) this is needed since we allocation buffer on resize // and the Gl Context should be properly set - makeCurrent(); + makeCurrentIfNeeded(); WaveformWidgetAbstract::resize(width, height); } void GLSLWaveformWidget::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { - makeCurrent(); + makeCurrentIfNeeded(); #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) if (m_signalRenderer) { m_signalRenderer->debugClick(); diff --git a/src/waveform/widgets/glvsynctestwidget.cpp b/src/waveform/widgets/glvsynctestwidget.cpp index 6b60e76977e..2df809bb6de 100644 --- a/src/waveform/widgets/glvsynctestwidget.cpp +++ b/src/waveform/widgets/glvsynctestwidget.cpp @@ -17,9 +17,9 @@ GLVSyncTestWidget::GLVSyncTestWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); // 172 µs // addRenderer(); // 677 µs 1145 µs (active) @@ -33,13 +33,7 @@ GLVSyncTestWidget::GLVSyncTestWidget(const QString& group, QWidget* parent) // addRenderer(); // 711 µs // addRenderer(); // 1183 µs - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); - qDebug() << "GLVSyncTestWidget.isSharing() =" << isSharing(); } GLVSyncTestWidget::~GLVSyncTestWidget() { @@ -60,7 +54,7 @@ mixxx::Duration GLVSyncTestWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/glwaveformwidget.cpp b/src/waveform/widgets/glwaveformwidget.cpp index f746ed70f29..b6f82974084 100644 --- a/src/waveform/widgets/glwaveformwidget.cpp +++ b/src/waveform/widgets/glwaveformwidget.cpp @@ -19,9 +19,9 @@ GLWaveformWidget::GLWaveformWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); addRenderer(); @@ -33,11 +33,6 @@ GLWaveformWidget::GLWaveformWidget(const QString& group, QWidget* parent) addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -59,7 +54,7 @@ mixxx::Duration GLWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/glwaveformwidget.h b/src/waveform/widgets/glwaveformwidget.h index 6787dc41a75..5f6d6673f8d 100644 --- a/src/waveform/widgets/glwaveformwidget.h +++ b/src/waveform/widgets/glwaveformwidget.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "waveform/widgets/glwaveformwidgetabstract.h" class GLWaveformWidget : public GLWaveformWidgetAbstract { diff --git a/src/waveform/widgets/glwaveformwidgetabstract.h b/src/waveform/widgets/glwaveformwidgetabstract.h index 420b0f001d2..fbd767f7897 100644 --- a/src/waveform/widgets/glwaveformwidgetabstract.h +++ b/src/waveform/widgets/glwaveformwidgetabstract.h @@ -1,24 +1,23 @@ #pragma once -#include - #include "waveform/renderers/glwaveformrenderer.h" -#include "waveform/sharedglcontext.h" #include "waveform/widgets/waveformwidgetabstract.h" +#include "widget/wglwidget.h" +#include "widget/wwaveformviewer.h" QT_FORWARD_DECLARE_CLASS(QString) -/// GLWaveformWidgetAbstract is a WaveformWidgetAbstract & QGLWidget. Its optional +/// GLWaveformWidgetAbstract is a WaveformWidgetAbstract & WGLWidget. Its optional /// member GLWaveformRenderer* m_pGlRenderer can implement a virtual method /// onInitializeGL, which will be called from GLWaveformRenderer::initializeGL -/// (which overrides QGLWidget::initializeGL). This can be used for initialization +/// (which overrides WGLWidget::initializeGL). This can be used for initialization /// that must be deferred until the GL context has been initialized and that can't /// be done in the constructor. -class GLWaveformWidgetAbstract : public WaveformWidgetAbstract, public QGLWidget { +class GLWaveformWidgetAbstract : public WaveformWidgetAbstract, public WGLWidget { public: GLWaveformWidgetAbstract(const QString& group, QWidget* parent) : WaveformWidgetAbstract(group), - QGLWidget(parent, SharedGLContext::getWidget()) + WGLWidget(parent) #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) , m_pGlRenderer(nullptr) @@ -26,6 +25,10 @@ class GLWaveformWidgetAbstract : public WaveformWidgetAbstract, public QGLWidget { } + WGLWidget* getGLWidget() override { + return this; + } + protected: #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) void initializeGL() override { @@ -34,6 +37,17 @@ class GLWaveformWidgetAbstract : public WaveformWidgetAbstract, public QGLWidget } } +#ifdef MIXXX_USE_QOPENGL + // We need to forward events coming from the QOpenGLWindow + // (drag&drop, mouse) to the viewer + void handleEventFromWindow(QEvent* ev) override { + auto viewer = dynamic_cast(parent()); + if (viewer) { + viewer->handleEventFromWindow(ev); + } + } +#endif + GLWaveformRenderer* m_pGlRenderer; #endif // !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) }; diff --git a/src/waveform/widgets/qthsvwaveformwidget.cpp b/src/waveform/widgets/qthsvwaveformwidget.cpp index 3292f518444..d886ef63bd4 100644 --- a/src/waveform/widgets/qthsvwaveformwidget.cpp +++ b/src/waveform/widgets/qthsvwaveformwidget.cpp @@ -16,9 +16,7 @@ QtHSVWaveformWidget::QtHSVWaveformWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - if (QGLContext::currentContext() != context()) { - makeCurrent(); - } + makeCurrentIfNeeded(); addRenderer(); addRenderer(); addRenderer(); @@ -27,9 +25,6 @@ QtHSVWaveformWidget::QtHSVWaveformWidget(const QString& group, QWidget* parent) addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - m_initSuccess = init(); } @@ -51,7 +46,7 @@ mixxx::Duration QtHSVWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/qtrgbwaveformwidget.cpp b/src/waveform/widgets/qtrgbwaveformwidget.cpp index 808898f486d..1f5b16dd836 100644 --- a/src/waveform/widgets/qtrgbwaveformwidget.cpp +++ b/src/waveform/widgets/qtrgbwaveformwidget.cpp @@ -13,13 +13,12 @@ #include "waveform/renderers/waveformrendermark.h" #include "waveform/renderers/waveformrendermarkrange.h" #include "waveform/renderers/waveformwidgetrenderer.h" -#include "waveform/sharedglcontext.h" QtRGBWaveformWidget::QtRGBWaveformWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); addRenderer(); @@ -29,11 +28,6 @@ QtRGBWaveformWidget::QtRGBWaveformWidget(const QString& group, QWidget* parent) addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -55,7 +49,7 @@ mixxx::Duration QtRGBWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/qtsimplewaveformwidget.cpp b/src/waveform/widgets/qtsimplewaveformwidget.cpp index bd645072124..2469dc5155e 100644 --- a/src/waveform/widgets/qtsimplewaveformwidget.cpp +++ b/src/waveform/widgets/qtsimplewaveformwidget.cpp @@ -13,15 +13,14 @@ #include "waveform/renderers/waveformrendermark.h" #include "waveform/renderers/waveformrendermarkrange.h" #include "waveform/renderers/waveformwidgetrenderer.h" -#include "waveform/sharedglcontext.h" QtSimpleWaveformWidget::QtSimpleWaveformWidget( const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); addRenderer(); @@ -31,11 +30,6 @@ QtSimpleWaveformWidget::QtSimpleWaveformWidget( addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -58,7 +52,7 @@ mixxx::Duration QtSimpleWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/qtvsynctestwidget.cpp b/src/waveform/widgets/qtvsynctestwidget.cpp index fd6928eb149..95519ec5144 100644 --- a/src/waveform/widgets/qtvsynctestwidget.cpp +++ b/src/waveform/widgets/qtvsynctestwidget.cpp @@ -14,21 +14,15 @@ #include "waveform/renderers/waveformrendermark.h" #include "waveform/renderers/waveformrendermarkrange.h" #include "waveform/renderers/waveformwidgetrenderer.h" -#include "waveform/sharedglcontext.h" QtVSyncTestWidget::QtVSyncTestWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -50,7 +44,7 @@ mixxx::Duration QtVSyncTestWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/qtwaveformwidget.cpp b/src/waveform/widgets/qtwaveformwidget.cpp index 16b880db6f6..fee4fb19aa5 100644 --- a/src/waveform/widgets/qtwaveformwidget.cpp +++ b/src/waveform/widgets/qtwaveformwidget.cpp @@ -14,13 +14,12 @@ #include "waveform/renderers/waveformrendermark.h" #include "waveform/renderers/waveformrendermarkrange.h" #include "waveform/renderers/waveformwidgetrenderer.h" -#include "waveform/sharedglcontext.h" QtWaveformWidget::QtWaveformWidget(const QString& group, QWidget* parent) : GLWaveformWidgetAbstract(group, parent) { - qDebug() << "Created QGLWidget. Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); + qDebug() << "Created WGLWidget. Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); addRenderer(); addRenderer(); @@ -30,11 +29,6 @@ QtWaveformWidget::QtWaveformWidget(const QString& group, QWidget* parent) addRenderer(); addRenderer(); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoBufferSwap(false); - m_initSuccess = init(); } @@ -56,7 +50,7 @@ mixxx::Duration QtWaveformWidget::render() { timer.start(); // QPainter makes QGLContext::currentContext() == context() // this may delayed until previous buffer swap finished - QPainter painter(this); + QPainter painter(paintDevice()); t1 = timer.restart(); draw(&painter, nullptr); //t2 = timer.restart(); diff --git a/src/waveform/widgets/waveformwidgetabstract.h b/src/waveform/widgets/waveformwidgetabstract.h index 52579775c60..0276184f3a6 100644 --- a/src/waveform/widgets/waveformwidgetabstract.h +++ b/src/waveform/widgets/waveformwidgetabstract.h @@ -6,12 +6,13 @@ #include "util/duration.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveformwidgettype.h" +#include "widget/wglwidget.h" class VSyncThread; // NOTE(vRince) This class represent objects the waveformwidgetfactory can // holds, IMPORTANT all WaveformWidgetAbstract MUST inherist QWidget too !! we -// can't do it here because QWidget and QGLWidget are both QWidgets so they +// can't do it here because QWidget and WGLWidget are both QWidgets so they // already have a common QWidget base class (ambiguous polymorphism) class WaveformWidgetAbstract : public WaveformWidgetRenderer { @@ -25,6 +26,10 @@ class WaveformWidgetAbstract : public WaveformWidgetRenderer { bool isValid() const { return (m_widget && m_initSuccess); } QWidget* getWidget() { return m_widget; } + virtual WGLWidget* getGLWidget() { + return nullptr; + } + void hold(); void release(); diff --git a/src/widget/openglwindow.cpp b/src/widget/openglwindow.cpp new file mode 100644 index 00000000000..743f595d044 --- /dev/null +++ b/src/widget/openglwindow.cpp @@ -0,0 +1,48 @@ +#include "widget/openglwindow.h" + +#include + +#include "widget/wglwidget.h" + +OpenGLWindow::OpenGLWindow(WGLWidget* widget) + : m_pWidget(widget) { +} + +OpenGLWindow::~OpenGLWindow() { +} + +void OpenGLWindow::initializeGL() { + if (m_pWidget) { + m_pWidget->initializeGL(); + } +} + +void OpenGLWindow::paintGL() { +} + +void OpenGLWindow::resizeGL(int w, int h) { +} + +void OpenGLWindow::widgetDestroyed() { + m_pWidget = nullptr; +} + +bool OpenGLWindow::event(QEvent* ev) { + bool result = QOpenGLWindow::event(ev); + + if (m_pWidget) { + const auto t = ev->type(); + if (t == QEvent::Expose) { + m_pWidget->exposed(); + } + // Forward the following events to the WGLWidget + else if (t == QEvent::MouseButtonDblClick || t == QEvent::MouseButtonPress || + t == QEvent::MouseButtonRelease || t == QEvent::MouseMove || + t == QEvent::DragEnter || t == QEvent::DragLeave || + t == QEvent::DragMove || t == QEvent::Drop) { + m_pWidget->handleEventFromWindow(ev); + } + } + + return result; +} diff --git a/src/widget/openglwindow.h b/src/widget/openglwindow.h new file mode 100644 index 00000000000..6e0d5e40a4d --- /dev/null +++ b/src/widget/openglwindow.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class WGLWidget; + +/// Helper class used by wglwidgetqopengl + +class OpenGLWindow : public QOpenGLWindow { + Q_OBJECT + + WGLWidget* m_pWidget; + + public: + OpenGLWindow(WGLWidget* widget); + ~OpenGLWindow(); + + void widgetDestroyed(); + + private: + void initializeGL() override; + void paintGL() override; + void resizeGL(int w, int h) override; + bool event(QEvent* ev) override; +}; diff --git a/src/widget/wglwidget.h b/src/widget/wglwidget.h new file mode 100644 index 00000000000..e66ef12aed0 --- /dev/null +++ b/src/widget/wglwidget.h @@ -0,0 +1,14 @@ +#pragma once + +// This define is checked in wglwidgetqglwidget.h and wglwidgetqglwidget.h +// to make sure they are only included from this header, to enforce that +// all code includes this header wglwidget.h. +#define WGLWIDGET_H + +#ifdef MIXXX_USE_QOPENGL +#include "widget/wglwidgetqopengl.h" +#else +#include "widget/wglwidgetqglwidget.h" +#endif + +#undef WGLWIDGET_H diff --git a/src/widget/wglwidgetqglwidget.cpp b/src/widget/wglwidgetqglwidget.cpp new file mode 100644 index 00000000000..40013e5c7b8 --- /dev/null +++ b/src/widget/wglwidgetqglwidget.cpp @@ -0,0 +1,34 @@ +#include + +#include "waveform/sharedglcontext.h" +#include "widget/wglwidget.h" + +WGLWidget::WGLWidget(QWidget* parent) + : QGLWidget(parent, SharedGLContext::getWidget()) { + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_OpaquePaintEvent); + setAutoFillBackground(false); + setAutoBufferSwap(false); + // Not interested in repaint or update calls, as we draw from the vsync thread + setUpdatesEnabled(false); +} + +bool WGLWidget::isContextValid() const { + return context()->isValid(); +} + +bool WGLWidget::isContextSharing() const { + return context()->isSharing(); +} + +bool WGLWidget::shouldRender() const { + return true; + return isValid() && isVisible() && windowHandle() && windowHandle()->isExposed(); +} + +void WGLWidget::makeCurrentIfNeeded() { + // TODO m0dB is this really needed? is calling makeCurrent without this 'if' really a problem? + //if (context() != QGLContext::currentContext()) { + makeCurrent(); + //} +} diff --git a/src/widget/wglwidgetqglwidget.h b/src/widget/wglwidgetqglwidget.h new file mode 100644 index 00000000000..6dc56872434 --- /dev/null +++ b/src/widget/wglwidgetqglwidget.h @@ -0,0 +1,27 @@ +#pragma once + +#ifndef WGLWIDGET_H +#error "Do not include this file, include wglwidget.h instead" +#endif + +#include + +/////////////////////////////////////// +// WGLWidget as a subclass of QGLWidget +/////////////////////////////////////// + +class WGLWidget : public QGLWidget { + public: + WGLWidget(QWidget* parent); + + bool isContextValid() const; + bool isContextSharing() const; + + bool shouldRender() const; + + void makeCurrentIfNeeded(); + + QPaintDevice* paintDevice() { + return this; + } +}; diff --git a/src/widget/wglwidgetqopengl.cpp b/src/widget/wglwidgetqopengl.cpp new file mode 100644 index 00000000000..aee622dc408 --- /dev/null +++ b/src/widget/wglwidgetqopengl.cpp @@ -0,0 +1,78 @@ +#include + +#include "widget/openglwindow.h" +#include "widget/wglwidget.h" + +WGLWidget::WGLWidget(QWidget* parent) + : QWidget(parent) { +} + +WGLWidget::~WGLWidget() { + if (m_pOpenGLWindow) { + m_pOpenGLWindow->widgetDestroyed(); + } +} + +QPaintDevice* WGLWidget::paintDevice() { + makeCurrentIfNeeded(); + return m_pOpenGLWindow; +} + +void WGLWidget::showEvent(QShowEvent* event) { + if (!m_pOpenGLWindow) { + m_pOpenGLWindow = new OpenGLWindow(this); + m_pContainerWidget = createWindowContainer(m_pOpenGLWindow, this); + m_pContainerWidget->resize(size()); + m_pContainerWidget->show(); + } + QWidget::showEvent(event); +} + +void WGLWidget::resizeEvent(QResizeEvent* event) { + if (m_pContainerWidget) { + m_pContainerWidget->resize(event->size()); + } +} + +void WGLWidget::handleEventFromWindow(QEvent* e) { + event(e); +} + +bool WGLWidget::isContextValid() const { + return m_pOpenGLWindow && m_pOpenGLWindow->context() && m_pOpenGLWindow->context()->isValid(); +} + +bool WGLWidget::isContextSharing() const { + return true; +} + +void WGLWidget::makeCurrentIfNeeded() { + if (m_pOpenGLWindow && m_pOpenGLWindow->context() != QOpenGLContext::currentContext()) { + m_pOpenGLWindow->makeCurrent(); + } +} + +void WGLWidget::doneCurrent() { + if (m_pOpenGLWindow) { + m_pOpenGLWindow->doneCurrent(); + } +} + +void WGLWidget::initializeGL() { + // to be implemented in derived widgets if needed +} + +void WGLWidget::exposed() { + // to be implemented in derived widgets if needed +} + +void WGLWidget::swapBuffers() { + if (shouldRender()) { + makeCurrentIfNeeded(); + m_pOpenGLWindow->context()->swapBuffers(m_pOpenGLWindow->context()->surface()); + } +} + +bool WGLWidget::shouldRender() const { + return m_pOpenGLWindow && m_pOpenGLWindow->isExposed(); +} diff --git a/src/widget/wglwidgetqopengl.h b/src/widget/wglwidgetqopengl.h new file mode 100644 index 00000000000..ea4961617fc --- /dev/null +++ b/src/widget/wglwidgetqopengl.h @@ -0,0 +1,45 @@ +#pragma once + +#ifndef WGLWIDGET_H +#error "Do not include this file, include wglwidget.h instead" +#endif + +#include + +//////////////////////////////// +// QOpenGLWindow based WGLWidget +//////////////////////////////// + +class QPaintDevice; +class OpenGLWindow; + +class WGLWidget : public QWidget { + private: + OpenGLWindow* m_pOpenGLWindow{}; + QWidget* m_pContainerWidget{}; + + public: + WGLWidget(QWidget* parent); + ~WGLWidget(); + + bool isContextValid() const; + bool isContextSharing() const; + + bool shouldRender() const; + + void makeCurrentIfNeeded(); + void doneCurrent(); + + void swapBuffers(); + void resizeEvent(QResizeEvent* event) override; + + void showEvent(QShowEvent* event) override; + + // called by OpenGLWindow + virtual void handleEventFromWindow(QEvent* ev); + virtual void initializeGL(); + virtual void exposed(); + + protected: + QPaintDevice* paintDevice(); +}; diff --git a/src/widget/wknob.cpp b/src/widget/wknob.cpp index f31d1d15ba7..8f24b4ab432 100644 --- a/src/widget/wknob.cpp +++ b/src/widget/wknob.cpp @@ -8,13 +8,18 @@ #include "util/duration.h" WKnob::WKnob(QWidget* pParent) - : WDisplay(pParent), + : WDisplay(pParent) +#ifdef USE_WIDGET_RENDER_TIMER + , m_renderTimer(mixxx::Duration::fromMillis(20), - mixxx::Duration::fromSeconds(1)) { + mixxx::Duration::fromSeconds(1)) { connect(&m_renderTimer, &WidgetRenderTimer::update, this, QOverload<>::of(&QWidget::update)); +#else +{ +#endif setFocusPolicy(Qt::NoFocus); } @@ -39,7 +44,7 @@ void WKnob::wheelEvent(QWheelEvent* e) { } void WKnob::inputActivity() { -#ifdef __APPLE__ +#ifdef USE_WIDGET_RENDER_TIMER m_renderTimer.activity(); #else update(); diff --git a/src/widget/wknob.h b/src/widget/wknob.h index 95ffe710794..be049ef5676 100644 --- a/src/widget/wknob.h +++ b/src/widget/wknob.h @@ -28,8 +28,9 @@ class WKnob : public WDisplay { void inputActivity(); private: +#ifdef USE_WIDGET_RENDER_TIMER WidgetRenderTimer m_renderTimer; - +#endif KnobEventHandler m_handler; friend class KnobEventHandler; }; diff --git a/src/widget/wknobcomposed.cpp b/src/widget/wknobcomposed.cpp index cc318ac80dd..a6736144b4b 100644 --- a/src/widget/wknobcomposed.cpp +++ b/src/widget/wknobcomposed.cpp @@ -20,13 +20,18 @@ WKnobComposed::WKnobComposed(QWidget* pParent) m_dArcBgThickness(0), m_arcUnipolar(true), m_arcReversed(false), - m_arcPenCap(Qt::FlatCap), + m_arcPenCap(Qt::FlatCap) +#ifdef USE_WIDGET_RENDER_TIMER + , m_renderTimer(mixxx::Duration::fromMillis(20), - mixxx::Duration::fromSeconds(1)) { + mixxx::Duration::fromSeconds(1)) { connect(&m_renderTimer, &WidgetRenderTimer::update, this, QOverload<>::of(&QWidget::update)); +#else +{ +#endif } void WKnobComposed::setup(const QDomNode& node, const SkinContext& context) { @@ -228,7 +233,7 @@ void WKnobComposed::wheelEvent(QWheelEvent* e) { } void WKnobComposed::inputActivity() { -#ifdef __APPLE__ +#ifdef USE_WIDGET_RENDER_TIMER m_renderTimer.activity(); #else update(); diff --git a/src/widget/wknobcomposed.h b/src/widget/wknobcomposed.h index cf3e287575c..861c024552d 100644 --- a/src/widget/wknobcomposed.h +++ b/src/widget/wknobcomposed.h @@ -63,7 +63,9 @@ class WKnobComposed : public WWidget { bool m_arcUnipolar; bool m_arcReversed; Qt::PenCapStyle m_arcPenCap; +#ifdef USE_WIDGET_RENDER_TIMER WidgetRenderTimer m_renderTimer; +#endif friend class KnobEventHandler; }; diff --git a/src/widget/wslidercomposed.cpp b/src/widget/wslidercomposed.cpp index 8f0c1574b29..eb18a4477b6 100644 --- a/src/widget/wslidercomposed.cpp +++ b/src/widget/wslidercomposed.cpp @@ -12,30 +12,35 @@ #include "widget/wpixmapstore.h" #include "widget/wskincolor.h" -WSliderComposed::WSliderComposed(QWidget * parent) - : WWidget(parent), - m_dHandleLength(0.0), - m_dSliderLength(0.0), - m_bHorizontal(false), - m_dBarWidth(0.0), - m_dBarBgWidth(0.0), - m_dBarStart(0.0), - m_dBarEnd(0.0), - m_dBarBgStart(0.0), - m_dBarBgEnd(0.0), - m_dBarAxisPos(0.0), - m_bBarUnipolar(true), - m_barColor(nullptr), - m_barBgColor(nullptr), - m_barPenCap(Qt::FlatCap), - m_pSlider(nullptr), - m_pHandle(nullptr), - m_renderTimer(mixxx::Duration::fromMillis(20), - mixxx::Duration::fromSeconds(1)) { +WSliderComposed::WSliderComposed(QWidget* parent) + : WWidget(parent), + m_dHandleLength(0.0), + m_dSliderLength(0.0), + m_bHorizontal(false), + m_dBarWidth(0.0), + m_dBarBgWidth(0.0), + m_dBarStart(0.0), + m_dBarEnd(0.0), + m_dBarBgStart(0.0), + m_dBarBgEnd(0.0), + m_dBarAxisPos(0.0), + m_bBarUnipolar(true), + m_barColor(nullptr), + m_barBgColor(nullptr), + m_barPenCap(Qt::FlatCap), + m_pSlider(nullptr), + m_pHandle(nullptr) +#ifdef USE_WIDGET_RENDER_TIMER + , + m_renderTimer(mixxx::Duration::fromMillis(20), + mixxx::Duration::fromSeconds(1)) { connect(&m_renderTimer, &WidgetRenderTimer::update, this, QOverload<>::of(&QWidget::update)); +#else + { +#endif } WSliderComposed::~WSliderComposed() { @@ -371,7 +376,7 @@ double WSliderComposed::calculateHandleLength() { } void WSliderComposed::inputActivity() { -#ifdef __APPLE__ +#ifdef USE_WIDGET_RENDER_TIMER m_renderTimer.activity(); #else update(); diff --git a/src/widget/wslidercomposed.h b/src/widget/wslidercomposed.h index 9162bba9946..1360eb16f10 100644 --- a/src/widget/wslidercomposed.h +++ b/src/widget/wslidercomposed.h @@ -75,7 +75,9 @@ class WSliderComposed : public WWidget { // Pointer to pixmap of the handle PaintablePointer m_pHandle; SliderEventHandler m_handler; +#ifdef USE_WIDGET_RENDER_TIMER WidgetRenderTimer m_renderTimer; +#endif friend class SliderEventHandler; }; diff --git a/src/widget/wspinny.cpp b/src/widget/wspinny.cpp index 1ae133983d2..21b89d20dfd 100644 --- a/src/widget/wspinny.cpp +++ b/src/widget/wspinny.cpp @@ -17,7 +17,6 @@ #include "util/fpclassify.h" #include "vinylcontrol/vinylcontrol.h" #include "vinylcontrol/vinylcontrolmanager.h" -#include "waveform/sharedglcontext.h" #include "waveform/visualplayposition.h" #include "waveform/vsyncthread.h" #include "wimagestore.h" @@ -29,7 +28,7 @@ WSpinny::WSpinny( UserSettingsPointer pConfig, VinylControlManager* pVCMan, BaseTrackPlayer* pPlayer) - : QGLWidget(parent, SharedGLContext::getWidget()), + : WGLWidget(parent), WBaseWidget(this), m_group(group), m_pConfig(pConfig), @@ -72,12 +71,10 @@ WSpinny::WSpinny( #endif // __VINYLCONTROL__ //Drag and drop setAcceptDrops(true); - qDebug() << "WSpinny(): Created QGLWidget, Context" - << "Valid:" << context()->isValid() - << "Sharing:" << context()->isSharing(); - if (QGLContext::currentContext() != context()) { - makeCurrent(); - } + qDebug() << "WSpinny(): Created WGLWidget, Context" + << "Valid:" << isContextValid() + << "Sharing:" << isContextSharing(); + makeCurrentIfNeeded(); CoverArtCache* pCache = CoverArtCache::instance(); if (pCache) { @@ -96,12 +93,6 @@ WSpinny::WSpinny( connect(m_pCoverMenu, &WCoverArtMenu::coverInfoSelected, this, &WSpinny::slotCoverInfoSelected); connect(m_pCoverMenu, &WCoverArtMenu::reloadCoverArt, this, &WSpinny::slotReloadCoverArt); - - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_OpaquePaintEvent); - - setAutoFillBackground(false); - setAutoBufferSwap(false); } WSpinny::~WSpinny() { @@ -328,12 +319,8 @@ void WSpinny::paintEvent(QPaintEvent *e) { } void WSpinny::render(VSyncThread* vSyncThread) { - if (!isValid() || !isVisible()) { - return; - } - - auto* window = windowHandle(); - if (window == nullptr || !window->isExposed()) { + // TODO @m0dB move outside? + if (!shouldRender()) { return; } @@ -346,7 +333,7 @@ void WSpinny::render(VSyncThread* vSyncThread) { double scaleFactor = devicePixelRatioF(); - QPainter p(this); + QPainter p(paintDevice()); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform); @@ -415,16 +402,11 @@ void WSpinny::render(VSyncThread* vSyncThread) { } void WSpinny::swap() { - if (!isValid() || !isVisible()) { - return; - } - auto* window = windowHandle(); - if (window == nullptr || !window->isExposed()) { + // TODO @m0dB move outside? + if (!shouldRender()) { return; } - if (context() != QGLContext::currentContext()) { - makeCurrent(); - } + makeCurrentIfNeeded(); swapBuffers(); } @@ -439,7 +421,7 @@ QPixmap WSpinny::scaledCoverArt(const QPixmap& normal) { return scaled; } -void WSpinny::resizeEvent(QResizeEvent* /*unused*/) { +void WSpinny::resizeEvent(QResizeEvent* event) { m_loadedCoverScaled = scaledCoverArt(m_loadedCover); if (m_pFgImage && !m_pFgImage->isNull()) { m_fgImageScaled = m_pFgImage->scaled( @@ -449,6 +431,7 @@ void WSpinny::resizeEvent(QResizeEvent* /*unused*/) { m_ghostImageScaled = m_pGhostImage->scaled( size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } + WGLWidget::resizeEvent(event); } /* Convert between a normalized playback position (0.0 - 1.0) and an angle @@ -691,6 +674,7 @@ void WSpinny::mouseReleaseEvent(QMouseEvent * e) void WSpinny::showEvent(QShowEvent* event) { Q_UNUSED(event); + WGLWidget::showEvent(event); #ifdef __VINYLCONTROL__ // If we want to draw the VC signal on this widget then register for // updates. @@ -716,7 +700,7 @@ bool WSpinny::event(QEvent* pEvent) { if (pEvent->type() == QEvent::ToolTip) { updateTooltip(); } - return QGLWidget::event(pEvent); + return WGLWidget::event(pEvent); } void WSpinny::dragEnterEvent(QDragEnterEvent* event) { diff --git a/src/widget/wspinny.h b/src/widget/wspinny.h index e632d4934f5..a24883ddd08 100644 --- a/src/widget/wspinny.h +++ b/src/widget/wspinny.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -14,6 +13,7 @@ #include "widget/trackdroptarget.h" #include "widget/wbasewidget.h" #include "widget/wcoverartmenu.h" +#include "widget/wglwidget.h" #include "widget/wwidget.h" class ConfigKey; @@ -22,7 +22,9 @@ class VisualPlayPosition; class VinylControlManager; class VSyncThread; -class WSpinny : public QGLWidget, public WBaseWidget, public VinylSignalQualityListener, +class WSpinny : public WGLWidget, + public WBaseWidget, + public VinylSignalQualityListener, public TrackDropTarget { Q_OBJECT public: diff --git a/src/widget/wvumetergl.cpp b/src/widget/wvumetergl.cpp index d973286eb2b..5bdacac771b 100644 --- a/src/widget/wvumetergl.cpp +++ b/src/widget/wvumetergl.cpp @@ -14,9 +14,9 @@ #define DEFAULT_HOLDSIZE 5 WVuMeterGL::WVuMeterGL(QWidget* parent) - : QGLWidget(parent, SharedGLContext::getWidget()), + : WGLWidget(parent), WBaseWidget(this), - m_bHasRendered(false), + m_iRendersPending(0), m_bSwapNeeded(false), m_dParameter(0), m_dPeakParameter(0), @@ -32,10 +32,6 @@ WVuMeterGL::WVuMeterGL(QWidget* parent) setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_OpaquePaintEvent); - setAutoFillBackground(false); - setAutoBufferSwap(false); - - // Not interested in repaint or update calls, as we draw from the vsync thread setUpdatesEnabled(false); } @@ -156,16 +152,15 @@ void WVuMeterGL::updateState(mixxx::Duration elapsed) { void WVuMeterGL::paintEvent(QPaintEvent* e) { Q_UNUSED(e); - // Force a rerender when render is called from the vsync thread, e.g. to - // git rid artifacts after hiding and showing the mixer or incomplete - // initial drawing. - m_bHasRendered = false; } void WVuMeterGL::showEvent(QShowEvent* e) { Q_UNUSED(e); + WGLWidget::showEvent(e); // Find the base color recursively in parent widget. m_qBgColor = mixxx::widgethelper::findBaseColor(this); + // 2 pendings renders, in cause we have triple buffering + m_iRendersPending = 2; } void WVuMeterGL::render(VSyncThread* vSyncThread) { @@ -173,21 +168,15 @@ void WVuMeterGL::render(VSyncThread* vSyncThread) { updateState(vSyncThread->sinceLastSwap()); - if (m_bHasRendered && m_dParameter == m_dLastParameter && - m_dPeakParameter == m_dLastPeakParameter) { - return; - } - - if (!isValid() || !isVisible()) { - return; + if (m_dParameter != m_dLastParameter || m_dPeakParameter != m_dLastPeakParameter) { + m_iRendersPending = 2; } - auto* window = windowHandle(); - if (window == nullptr || !window->isExposed()) { + if (m_iRendersPending == 0 || !shouldRender()) { return; } - QPainter p(this); + QPainter p(paintDevice()); // fill the background, in case the image contains transparency p.fillRect(rect(), m_qBgColor); @@ -294,21 +283,15 @@ void WVuMeterGL::render(VSyncThread* vSyncThread) { m_dLastParameter = m_dParameter; m_dLastPeakParameter = m_dPeakParameter; - m_bHasRendered = true; + m_iRendersPending--; m_bSwapNeeded = true; } void WVuMeterGL::swap() { - if (!isValid() || !isVisible() || !m_bSwapNeeded) { - return; - } - auto* window = windowHandle(); - if (window == nullptr || !window->isExposed()) { + if (!m_bSwapNeeded || !shouldRender()) { return; } - if (context() != QGLContext::currentContext()) { - makeCurrent(); - } + makeCurrentIfNeeded(); swapBuffers(); m_bSwapNeeded = false; } diff --git a/src/widget/wvumetergl.h b/src/widget/wvumetergl.h index 43437489d80..b5d7bb72928 100644 --- a/src/widget/wvumetergl.h +++ b/src/widget/wvumetergl.h @@ -1,15 +1,14 @@ #pragma once -#include - #include "skin/legacy/skincontext.h" #include "util/duration.h" +#include "widget/wglwidget.h" #include "widget/wpixmapstore.h" #include "widget/wwidget.h" class VSyncThread; -class WVuMeterGL : public QGLWidget, public WBaseWidget { +class WVuMeterGL : public WGLWidget, public WBaseWidget { Q_OBJECT public: explicit WVuMeterGL(QWidget* parent = nullptr); @@ -39,7 +38,7 @@ class WVuMeterGL : public QGLWidget, public WBaseWidget { void setPeak(double parameter); // To make sure we render at least once even when we have no signal - bool m_bHasRendered; + int m_iRendersPending; // To indicate that we rendered so we need to swap bool m_bSwapNeeded; // Current parameter and peak parameter. diff --git a/src/widget/wwaveformviewer.h b/src/widget/wwaveformviewer.h index 4afbe391d7d..309974877ea 100644 --- a/src/widget/wwaveformviewer.h +++ b/src/widget/wwaveformviewer.h @@ -40,6 +40,13 @@ class WWaveformViewer : public WWidget, public TrackDropTarget { void mouseReleaseEvent(QMouseEvent * /*unused*/) override; void leaveEvent(QEvent* /*unused*/) override; +#ifdef MIXXX_USE_QOPENGL + // Handle the event forwarded from the QOpenGLWindow + void handleEventFromWindow(QEvent* e) { + event(e); + } +#endif + signals: void trackDropped(const QString& filename, const QString& group) override; void cloneDeck(const QString& sourceGroup, const QString& targetGroup) override;