Commit c19372354698f6689f50be839fdb27e4e8449e5c
Committed by
David Gräff
1 parent
0f3e1d63
Use OpenGL 3.2 instead of 3.3 to make it work on MacOSX
Fix: * Move all GL startup calls to GLScope::fixOpenGLversion * Depending on OpenGL or ES availability initialize the QSurfaceFormat * Don't crash if the shader compilation failed. Show a nice message instead. Optimize: * Don't write the markers to the GPU each frame update, only do this if they actually changed position. Code style: * LevelSlider: Don't return a value for set* Methods. * Don't rely on the LevelSliders step to calculate the marker position in the OpenGL view.
Showing
8 changed files
with
197 additions
and
136 deletions
openhantek/src/dsowidget.cpp
| ... | ... | @@ -5,8 +5,8 @@ |
| 5 | 5 | #include <QFileDialog> |
| 6 | 6 | #include <QGridLayout> |
| 7 | 7 | #include <QLabel> |
| 8 | -#include <QTimer> | |
| 9 | 8 | #include <QSignalBlocker> |
| 9 | +#include <QTimer> | |
| 10 | 10 | |
| 11 | 11 | #include "dsowidget.h" |
| 12 | 12 | |
| ... | ... | @@ -37,11 +37,12 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: |
| 37 | 37 | setupSliders(mainSliders); |
| 38 | 38 | setupSliders(zoomSliders); |
| 39 | 39 | |
| 40 | - connect(mainScope, &GlScope::markerMoved, [this] (int marker, double position) { | |
| 41 | - double step = this->mainSliders.markerSlider->step(marker); | |
| 40 | + connect(mainScope, &GlScope::markerMoved, [this](int marker, double position) { | |
| 41 | + double value = std::round(position / MARKER_STEP) * MARKER_STEP; | |
| 42 | 42 | |
| 43 | - this->scope->horizontal.marker[marker] = | |
| 44 | - this->mainSliders.markerSlider->setValue(marker, std::round(position / step) * step); | |
| 43 | + this->scope->horizontal.marker[marker] = value; | |
| 44 | + this->mainSliders.markerSlider->setValue(marker, value); | |
| 45 | + this->mainScope->markerUpdated(); | |
| 45 | 46 | }); |
| 46 | 47 | |
| 47 | 48 | // The table for the settings |
| ... | ... | @@ -175,14 +176,6 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: |
| 175 | 176 | mainLayout->setRowMinimumHeight(row++, 8); |
| 176 | 177 | mainLayout->addLayout(measurementLayout, row++, 0, 1, 5); |
| 177 | 178 | |
| 178 | - // Apply settings and update measured values | |
| 179 | - updateTriggerDetails(); | |
| 180 | - updateRecordLength(scope->horizontal.recordLength); | |
| 181 | - updateFrequencybase(scope->horizontal.frequencybase); | |
| 182 | - updateSamplerate(scope->horizontal.samplerate); | |
| 183 | - updateTimebase(scope->horizontal.timebase); | |
| 184 | - updateZoom(view->zoom); | |
| 185 | - | |
| 186 | 179 | // The widget itself |
| 187 | 180 | setPalette(palette); |
| 188 | 181 | setBackgroundRole(QPalette::Background); |
| ... | ... | @@ -205,9 +198,6 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: |
| 205 | 198 | zoomScope->update(); |
| 206 | 199 | }); |
| 207 | 200 | zoomSliders.markerSlider->setEnabled(false); |
| 208 | - | |
| 209 | - updateTriggerSource(); | |
| 210 | - adaptTriggerPositionSlider(); | |
| 211 | 201 | } |
| 212 | 202 | |
| 213 | 203 | void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { |
| ... | ... | @@ -242,11 +232,10 @@ void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { |
| 242 | 232 | sliders.triggerLevelSlider = new LevelSlider(Qt::LeftArrow); |
| 243 | 233 | for (ChannelID channel = 0; channel < spec->channels; ++channel) { |
| 244 | 234 | sliders.triggerLevelSlider->addSlider((int)channel); |
| 245 | - sliders.triggerLevelSlider->setColor( | |
| 246 | - channel, | |
| 247 | - (!scope->trigger.special && channel == scope->trigger.source) | |
| 248 | - ? view->screen.voltage[channel] | |
| 249 | - : view->screen.voltage[channel].darker()); | |
| 235 | + sliders.triggerLevelSlider->setColor(channel, | |
| 236 | + (!scope->trigger.special && channel == scope->trigger.source) | |
| 237 | + ? view->screen.voltage[channel] | |
| 238 | + : view->screen.voltage[channel].darker()); | |
| 250 | 239 | adaptTriggerLevelSlider(sliders, channel); |
| 251 | 240 | sliders.triggerLevelSlider->setValue(channel, scope->voltage[channel].trigger); |
| 252 | 241 | sliders.triggerLevelSlider->setIndexVisible(channel, scope->voltage[channel].used); |
| ... | ... | @@ -257,7 +246,7 @@ void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { |
| 257 | 246 | for (int marker = 0; marker < MARKER_COUNT; ++marker) { |
| 258 | 247 | sliders.markerSlider->addSlider(QString::number(marker + 1), marker); |
| 259 | 248 | sliders.markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2); |
| 260 | - sliders.markerSlider->setStep(marker, DIVS_TIME / 100.0); | |
| 249 | + sliders.markerSlider->setStep(marker, MARKER_STEP); | |
| 261 | 250 | sliders.markerSlider->setValue(marker, scope->horizontal.marker[marker]); |
| 262 | 251 | sliders.markerSlider->setIndexVisible(marker, true); |
| 263 | 252 | } |
| ... | ... | @@ -269,9 +258,9 @@ void DsoWidget::setExporterForNextFrame(std::unique_ptr<Exporter> exporter) { |
| 269 | 258 | |
| 270 | 259 | /// \brief Set the trigger level sliders minimum and maximum to the new values. |
| 271 | 260 | void DsoWidget::adaptTriggerLevelSlider(DsoWidget::Sliders &sliders, ChannelID channel) { |
| 272 | - sliders.triggerLevelSlider->setLimits( | |
| 273 | - (int)channel, (-DIVS_VOLTAGE / 2 - scope->voltage[channel].offset) * scope->gain(channel), | |
| 274 | - (DIVS_VOLTAGE / 2 - scope->voltage[channel].offset) * scope->gain(channel)); | |
| 261 | + sliders.triggerLevelSlider->setLimits((int)channel, | |
| 262 | + (-DIVS_VOLTAGE / 2 - scope->voltage[channel].offset) * scope->gain(channel), | |
| 263 | + (DIVS_VOLTAGE / 2 - scope->voltage[channel].offset) * scope->gain(channel)); | |
| 275 | 264 | sliders.triggerLevelSlider->setStep((int)channel, scope->gain(channel) * 0.05); |
| 276 | 265 | } |
| 277 | 266 | |
| ... | ... | @@ -292,14 +281,10 @@ void DsoWidget::setMeasurementVisible(ChannelID channel) { |
| 292 | 281 | } |
| 293 | 282 | |
| 294 | 283 | measurementGainLabel[channel]->setVisible(scope->voltage[channel].used); |
| 295 | - if (!scope->voltage[channel].used) { | |
| 296 | - measurementGainLabel[channel]->setText(QString()); | |
| 297 | - } | |
| 284 | + if (!scope->voltage[channel].used) { measurementGainLabel[channel]->setText(QString()); } | |
| 298 | 285 | |
| 299 | 286 | measurementMagnitudeLabel[channel]->setVisible(scope->spectrum[channel].used); |
| 300 | - if (!scope->spectrum[channel].used) { | |
| 301 | - measurementMagnitudeLabel[channel]->setText(QString()); | |
| 302 | - } | |
| 287 | + if (!scope->spectrum[channel].used) { measurementMagnitudeLabel[channel]->setText(QString()); } | |
| 303 | 288 | } |
| 304 | 289 | |
| 305 | 290 | /// \brief Update the label about the marker measurements |
| ... | ... | @@ -411,16 +396,15 @@ void DsoWidget::updateTriggerSource() { |
| 411 | 396 | if (scope->trigger.special || scope->trigger.source >= spec->channels) { |
| 412 | 397 | mainSliders.triggerPositionSlider->setColor(0, view->screen.border); |
| 413 | 398 | zoomSliders.triggerPositionSlider->setColor(0, view->screen.border); |
| 414 | - } | |
| 415 | - else { | |
| 399 | + } else { | |
| 416 | 400 | mainSliders.triggerPositionSlider->setColor(0, view->screen.voltage[scope->trigger.source]); |
| 417 | 401 | zoomSliders.triggerPositionSlider->setColor(0, view->screen.voltage[scope->trigger.source]); |
| 418 | 402 | } |
| 419 | 403 | |
| 420 | 404 | for (ChannelID channel = 0; channel < spec->channels; ++channel) { |
| 421 | 405 | QColor color = (!scope->trigger.special && channel == scope->trigger.source) |
| 422 | - ? view->screen.voltage[channel] | |
| 423 | - : view->screen.voltage[channel].darker(); | |
| 406 | + ? view->screen.voltage[channel] | |
| 407 | + : view->screen.voltage[channel].darker(); | |
| 424 | 408 | mainSliders.triggerLevelSlider->setColor(channel, color); |
| 425 | 409 | zoomSliders.triggerLevelSlider->setColor(channel, color); |
| 426 | 410 | } |
| ... | ... | @@ -484,8 +468,7 @@ void DsoWidget::updateZoom(bool enabled) { |
| 484 | 468 | zoomSliders.offsetSlider->show(); |
| 485 | 469 | zoomSliders.triggerPositionSlider->show(); |
| 486 | 470 | zoomSliders.triggerLevelSlider->show(); |
| 487 | - } | |
| 488 | - else { | |
| 471 | + } else { | |
| 489 | 472 | zoomSliders.offsetSlider->hide(); |
| 490 | 473 | zoomSliders.triggerPositionSlider->hide(); |
| 491 | 474 | zoomSliders.triggerLevelSlider->hide(); |
| ... | ... | @@ -535,6 +518,20 @@ void DsoWidget::showNew(std::shared_ptr<PPresult> data) { |
| 535 | 518 | } |
| 536 | 519 | } |
| 537 | 520 | |
| 521 | +void DsoWidget::showEvent(QShowEvent *event) { | |
| 522 | + QWidget::showEvent(event); | |
| 523 | + // Apply settings and update measured values | |
| 524 | + updateTriggerDetails(); | |
| 525 | + updateRecordLength(scope->horizontal.recordLength); | |
| 526 | + updateFrequencybase(scope->horizontal.frequencybase); | |
| 527 | + updateSamplerate(scope->horizontal.samplerate); | |
| 528 | + updateTimebase(scope->horizontal.timebase); | |
| 529 | + updateZoom(view->zoom); | |
| 530 | + | |
| 531 | + updateTriggerSource(); | |
| 532 | + adaptTriggerPositionSlider(); | |
| 533 | +} | |
| 534 | + | |
| 538 | 535 | /// \brief Handles valueChanged signal from the offset sliders. |
| 539 | 536 | /// \param channel The channel whose offset was changed. |
| 540 | 537 | /// \param value The new offset for the channel. |
| ... | ... | @@ -573,8 +570,7 @@ void DsoWidget::adaptTriggerPositionSlider() { |
| 573 | 570 | if (m1 != m2 && m1 <= value && value <= m2) { |
| 574 | 571 | zoomSliders.triggerPositionSlider->setIndexVisible(0, true); |
| 575 | 572 | zoomSliders.triggerPositionSlider->setValue(0, (value - m1) / (m2 - m1)); |
| 576 | - } | |
| 577 | - else { | |
| 573 | + } else { | |
| 578 | 574 | zoomSliders.triggerPositionSlider->setIndexVisible(0, false); |
| 579 | 575 | } |
| 580 | 576 | } | ... | ... |
openhantek/src/dsowidget.h
| ... | ... | @@ -42,6 +42,7 @@ class DsoWidget : public QWidget { |
| 42 | 42 | void showNew(std::shared_ptr<PPresult> data); |
| 43 | 43 | |
| 44 | 44 | protected: |
| 45 | + virtual void showEvent(QShowEvent *event); | |
| 45 | 46 | void setupSliders(Sliders &sliders); |
| 46 | 47 | void adaptTriggerLevelSlider(DsoWidget::Sliders &sliders, ChannelID channel); |
| 47 | 48 | void adaptTriggerPositionSlider(); | ... | ... |
openhantek/src/glscope.cpp
| ... | ... | @@ -4,12 +4,16 @@ |
| 4 | 4 | #include <iostream> |
| 5 | 5 | |
| 6 | 6 | #include <QColor> |
| 7 | +#include <QCoreApplication> | |
| 7 | 8 | #include <QDebug> |
| 8 | 9 | #include <QMatrix4x4> |
| 10 | +#include <QMessageBox> | |
| 9 | 11 | #include <QMouseEvent> |
| 10 | 12 | #include <QOpenGLShaderProgram> |
| 13 | +#include <QPainter> | |
| 11 | 14 | |
| 12 | -#include <QOpenGLFunctions_3_3_Core> | |
| 15 | +// We can't be more modern than OpenGL 3.2 or ES2 because of MacOSX. | |
| 16 | +#include <QOpenGLFunctions_3_2_Core> | |
| 13 | 17 | #include <QOpenGLFunctions_ES2> |
| 14 | 18 | |
| 15 | 19 | #include "glscope.h" |
| ... | ... | @@ -23,7 +27,7 @@ |
| 23 | 27 | #if defined(QT_OPENGL_ES_2) |
| 24 | 28 | typedef QOpenGLFunctions_ES2 OPENGL_VER; |
| 25 | 29 | #else |
| 26 | -typedef QOpenGLFunctions_3_3_Core OPENGL_VER; | |
| 30 | +typedef QOpenGLFunctions_3_2_Core OPENGL_VER; | |
| 27 | 31 | #endif |
| 28 | 32 | |
| 29 | 33 | GlScope *GlScope::createNormal(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) { |
| ... | ... | @@ -38,6 +42,25 @@ GlScope *GlScope::createZoomed(DsoSettingsScope *scope, DsoSettingsView *view, Q |
| 38 | 42 | return s; |
| 39 | 43 | } |
| 40 | 44 | |
| 45 | +void GlScope::fixOpenGLversion() { | |
| 46 | + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); | |
| 47 | + | |
| 48 | + // Prefer full desktop OpenGL without fixed pipeline | |
| 49 | + QSurfaceFormat format; | |
| 50 | + format.setSamples(4); // Antia-Aliasing, Multisampling | |
| 51 | +#if defined(QT_OPENGL_ES_2) | |
| 52 | + format.setVersion(2, 0); | |
| 53 | + format.setProfile(QSurfaceFormat::CoreProfile); | |
| 54 | + format.setRenderableType(QSurfaceFormat::OpenGLES); | |
| 55 | + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); | |
| 56 | +#else | |
| 57 | + format.setVersion(3, 2); | |
| 58 | + format.setProfile(QSurfaceFormat::CoreProfile); | |
| 59 | + format.setRenderableType(QSurfaceFormat::OpenGL); | |
| 60 | +#endif | |
| 61 | + QSurfaceFormat::setDefaultFormat(format); | |
| 62 | +} | |
| 63 | + | |
| 41 | 64 | GlScope::GlScope(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) |
| 42 | 65 | : QOpenGLWidget(parent), scope(scope), view(view) { |
| 43 | 66 | vaMarker.resize(MARKER_COUNT); |
| ... | ... | @@ -51,7 +74,7 @@ void GlScope::mousePressEvent(QMouseEvent *event) { |
| 51 | 74 | double distance = DIVS_TIME; |
| 52 | 75 | selectedMarker = NO_MARKER; |
| 53 | 76 | // Capture nearest marker located within snap area (+/- 1% of full scale). |
| 54 | - for (int marker = 0; marker < MARKER_COUNT; ++marker) { | |
| 77 | + for (unsigned marker = 0; marker < MARKER_COUNT; ++marker) { | |
| 55 | 78 | if (!scope->horizontal.marker_visible[marker]) continue; |
| 56 | 79 | if (fabs(scope->horizontal.marker[marker] - position) < std::min(distance, DIVS_TIME / 100.0)) { |
| 57 | 80 | distance = fabs(scope->horizontal.marker[marker] - position); |
| ... | ... | @@ -89,13 +112,34 @@ void GlScope::mouseReleaseEvent(QMouseEvent *event) { |
| 89 | 112 | event->accept(); |
| 90 | 113 | } |
| 91 | 114 | |
| 115 | +void GlScope::paintEvent(QPaintEvent *event) { | |
| 116 | + // Draw error message if OpenGL failed | |
| 117 | + if (!shaderCompileSuccess) { | |
| 118 | + QPainter painter(this); | |
| 119 | + painter.setRenderHint(QPainter::Antialiasing, true); | |
| 120 | + QFont font = painter.font(); | |
| 121 | + font.setPointSize(18); | |
| 122 | + painter.setFont(font); | |
| 123 | + painter.drawText(rect(), Qt::AlignCenter, errorMessage); | |
| 124 | + event->accept(); | |
| 125 | + } else | |
| 126 | + QOpenGLWidget::paintEvent(event); | |
| 127 | +} | |
| 128 | + | |
| 92 | 129 | void GlScope::initializeGL() { |
| 93 | - auto *gl = context()->versionFunctions<OPENGL_VER>(); | |
| 130 | + if (!QOpenGLShaderProgram::hasOpenGLShaderPrograms(context())) { | |
| 131 | + errorMessage = tr("System does not support OpenGL Shading Language (GLSL)"); | |
| 132 | + return; | |
| 133 | + } | |
| 134 | + if (m_program) { | |
| 135 | + qWarning() << "OpenGL init called twice!"; | |
| 136 | + return; | |
| 137 | + } | |
| 94 | 138 | |
| 95 | - m_program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram(context())); | |
| 139 | + auto program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram(context())); | |
| 96 | 140 | |
| 97 | 141 | const char *vshaderES = R"( |
| 98 | - #version 330 | |
| 142 | + #version 100 | |
| 99 | 143 | attribute highp vec3 vertex; |
| 100 | 144 | uniform mat4 matrix; |
| 101 | 145 | void main() |
| ... | ... | @@ -104,9 +148,14 @@ void GlScope::initializeGL() { |
| 104 | 148 | gl_PointSize = 1.0; |
| 105 | 149 | } |
| 106 | 150 | )"; |
| 151 | + const char *fshaderES = R"( | |
| 152 | + #version 100 | |
| 153 | + uniform highp vec4 colour; | |
| 154 | + void main() { gl_FragColor = colour; } | |
| 155 | + )"; | |
| 107 | 156 | |
| 108 | 157 | const char *vshaderCore = R"( |
| 109 | - #version 330 | |
| 158 | + #version 150 | |
| 110 | 159 | in highp vec3 vertex; |
| 111 | 160 | uniform mat4 matrix; |
| 112 | 161 | void main() |
| ... | ... | @@ -116,37 +165,39 @@ void GlScope::initializeGL() { |
| 116 | 165 | } |
| 117 | 166 | )"; |
| 118 | 167 | const char *fshaderCore = R"( |
| 119 | - #version 330 | |
| 168 | + #version 150 | |
| 120 | 169 | uniform highp vec4 colour; |
| 121 | - void main() { gl_FragColor = colour; } | |
| 170 | + out vec4 flatColor; | |
| 171 | + void main() { flatColor = colour; } | |
| 122 | 172 | )"; |
| 123 | 173 | |
| 124 | 174 | qDebug() << "compile shaders"; |
| 125 | 175 | // Compile vertex shader |
| 126 | 176 | bool coreShaders = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile; |
| 127 | - if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, coreShaders ? vshaderCore : vshaderES) || | |
| 128 | - !m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fshaderCore)) { | |
| 129 | - qWarning() << "Failed to compile OpenGL shader programs.\n" << m_program->log(); | |
| 177 | + if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, coreShaders ? vshaderCore : vshaderES) || | |
| 178 | + !program->addShaderFromSourceCode(QOpenGLShader::Fragment, coreShaders ? fshaderCore : fshaderES)) { | |
| 179 | + errorMessage = "Failed to compile OpenGL shader programs.\n" + program->log(); | |
| 130 | 180 | return; |
| 131 | 181 | } |
| 132 | 182 | |
| 133 | 183 | // Link shader pipeline |
| 134 | - if (!m_program->link() || !m_program->bind()) { | |
| 135 | - qWarning() << "Failed to link/bind OpenGL shader programs"; | |
| 184 | + if (!program->link() || !program->bind()) { | |
| 185 | + errorMessage = "Failed to link/bind OpenGL shader programs\n" + program->log(); | |
| 136 | 186 | return; |
| 137 | 187 | } |
| 138 | 188 | |
| 139 | - vertexLocation = m_program->attributeLocation("vertex"); | |
| 140 | - matrixLocation = m_program->uniformLocation("matrix"); | |
| 141 | - colorLocation = m_program->uniformLocation("colour"); | |
| 189 | + vertexLocation = program->attributeLocation("vertex"); | |
| 190 | + matrixLocation = program->uniformLocation("matrix"); | |
| 191 | + colorLocation = program->uniformLocation("colour"); | |
| 142 | 192 | |
| 143 | 193 | if (vertexLocation == -1 || colorLocation == -1 || matrixLocation == -1) { |
| 144 | 194 | qWarning() << "Failed to locate shader variable"; |
| 145 | 195 | return; |
| 146 | 196 | } |
| 147 | 197 | |
| 148 | - m_program->bind(); | |
| 198 | + program->bind(); | |
| 149 | 199 | |
| 200 | + auto *gl = context()->versionFunctions<OPENGL_VER>(); | |
| 150 | 201 | gl->glDisable(GL_DEPTH_TEST); |
| 151 | 202 | gl->glEnable(GL_BLEND); |
| 152 | 203 | // Enable depth buffer |
| ... | ... | @@ -159,7 +210,7 @@ void GlScope::initializeGL() { |
| 159 | 210 | QColor bg = view->screen.background; |
| 160 | 211 | gl->glClearColor((GLfloat)bg.redF(), (GLfloat)bg.greenF(), (GLfloat)bg.blueF(), (GLfloat)bg.alphaF()); |
| 161 | 212 | |
| 162 | - generateGrid(); | |
| 213 | + generateGrid(program.get()); | |
| 163 | 214 | |
| 164 | 215 | { |
| 165 | 216 | m_vaoMarker.create(); |
| ... | ... | @@ -168,15 +219,18 @@ void GlScope::initializeGL() { |
| 168 | 219 | m_marker.bind(); |
| 169 | 220 | m_marker.setUsagePattern(QOpenGLBuffer::StaticDraw); |
| 170 | 221 | m_marker.allocate(int(vaMarker.size() * sizeof(Line))); |
| 171 | - m_program->enableAttributeArray(vertexLocation); | |
| 172 | - m_program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); | |
| 222 | + program->enableAttributeArray(vertexLocation); | |
| 223 | + program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); | |
| 173 | 224 | } |
| 174 | 225 | |
| 175 | - m_program->release(); | |
| 226 | + markerUpdated(); | |
| 227 | + | |
| 228 | + m_program = std::move(program); | |
| 229 | + shaderCompileSuccess = true; | |
| 176 | 230 | } |
| 177 | 231 | |
| 178 | 232 | void GlScope::showData(PPresult *data) { |
| 179 | - if (!m_program || !m_program->isLinked()) return; | |
| 233 | + if (!shaderCompileSuccess) return; | |
| 180 | 234 | makeCurrent(); |
| 181 | 235 | // Remove too much entries |
| 182 | 236 | while (view->digitalPhosphorDraws() < m_GraphHistory.size()) m_GraphHistory.pop_back(); |
| ... | ... | @@ -192,8 +246,23 @@ void GlScope::showData(PPresult *data) { |
| 192 | 246 | // doneCurrent(); |
| 193 | 247 | } |
| 194 | 248 | |
| 249 | +void GlScope::markerUpdated() | |
| 250 | +{ | |
| 251 | + | |
| 252 | + for (unsigned marker = 0; marker < vaMarker.size(); ++marker) { | |
| 253 | + if (!scope->horizontal.marker_visible[marker]) continue; | |
| 254 | + vaMarker[marker] = {QVector3D((GLfloat)scope->horizontal.marker[marker], -DIVS_VOLTAGE, 0.0f), | |
| 255 | + QVector3D((GLfloat)scope->horizontal.marker[marker], DIVS_VOLTAGE, 0.0f)}; | |
| 256 | + } | |
| 257 | + | |
| 258 | + // Write coordinates to GPU | |
| 259 | + makeCurrent(); | |
| 260 | + m_marker.bind(); | |
| 261 | + m_marker.write(0, vaMarker.data(), vaMarker.size() * sizeof(Line)); | |
| 262 | +} | |
| 263 | + | |
| 195 | 264 | void GlScope::paintGL() { |
| 196 | - if (!m_program->isLinked()) return; | |
| 265 | + if (!shaderCompileSuccess) return; | |
| 197 | 266 | |
| 198 | 267 | auto *gl = context()->versionFunctions<OPENGL_VER>(); |
| 199 | 268 | |
| ... | ... | @@ -231,6 +300,7 @@ void GlScope::paintGL() { |
| 231 | 300 | } |
| 232 | 301 | |
| 233 | 302 | void GlScope::resizeGL(int width, int height) { |
| 303 | + if (!shaderCompileSuccess) return; | |
| 234 | 304 | auto *gl = context()->versionFunctions<OPENGL_VER>(); |
| 235 | 305 | gl->glViewport(0, 0, (GLint)width, (GLint)height); |
| 236 | 306 | |
| ... | ... | @@ -248,30 +318,7 @@ void GlScope::resizeGL(int width, int height) { |
| 248 | 318 | m_program->release(); |
| 249 | 319 | } |
| 250 | 320 | |
| 251 | -void GlScope::drawGrid() { | |
| 252 | - auto *gl = context()->versionFunctions<OPENGL_VER>(); | |
| 253 | - gl->glLineWidth(1); | |
| 254 | - | |
| 255 | - // Grid | |
| 256 | - m_vaoGrid[0].bind(); | |
| 257 | - m_program->setUniformValue(colorLocation, view->screen.grid); | |
| 258 | - gl->glDrawArrays(GL_POINTS, 0, gridDrawCounts[0]); | |
| 259 | - m_vaoGrid[0].release(); | |
| 260 | - | |
| 261 | - // Axes | |
| 262 | - m_vaoGrid[1].bind(); | |
| 263 | - m_program->setUniformValue(colorLocation, view->screen.axes); | |
| 264 | - gl->glDrawArrays(GL_LINES, 0, gridDrawCounts[1]); | |
| 265 | - m_vaoGrid[1].release(); | |
| 266 | - | |
| 267 | - // Border | |
| 268 | - m_vaoGrid[2].bind(); | |
| 269 | - m_program->setUniformValue(colorLocation, view->screen.border); | |
| 270 | - gl->glDrawArrays(GL_LINE_LOOP, 0, gridDrawCounts[2]); | |
| 271 | - m_vaoGrid[2].release(); | |
| 272 | -} | |
| 273 | - | |
| 274 | -void GlScope::generateGrid() { | |
| 321 | +void GlScope::generateGrid(QOpenGLShaderProgram *program) { | |
| 275 | 322 | gridDrawCounts[0] = 0; |
| 276 | 323 | gridDrawCounts[1] = 0; |
| 277 | 324 | gridDrawCounts[2] = 0; |
| ... | ... | @@ -286,8 +333,8 @@ void GlScope::generateGrid() { |
| 286 | 333 | m_vaoGrid[0].create(); |
| 287 | 334 | QOpenGLVertexArrayObject::Binder b(&m_vaoGrid[0]); |
| 288 | 335 | m_grid.bind(); |
| 289 | - m_program->enableAttributeArray(vertexLocation); | |
| 290 | - m_program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); | |
| 336 | + program->enableAttributeArray(vertexLocation); | |
| 337 | + program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); | |
| 291 | 338 | } |
| 292 | 339 | |
| 293 | 340 | // Draw vertical lines |
| ... | ... | @@ -318,8 +365,8 @@ void GlScope::generateGrid() { |
| 318 | 365 | m_vaoGrid[1].create(); |
| 319 | 366 | QOpenGLVertexArrayObject::Binder b(&m_vaoGrid[1]); |
| 320 | 367 | m_grid.bind(); |
| 321 | - m_program->enableAttributeArray(vertexLocation); | |
| 322 | - m_program->setAttributeBuffer(vertexLocation, GL_FLOAT, int(vaGrid.size() * sizeof(QVector3D)), 3); | |
| 368 | + program->enableAttributeArray(vertexLocation); | |
| 369 | + program->setAttributeBuffer(vertexLocation, GL_FLOAT, int(vaGrid.size() * sizeof(QVector3D)), 3); | |
| 323 | 370 | } |
| 324 | 371 | |
| 325 | 372 | // Axes |
| ... | ... | @@ -353,8 +400,8 @@ void GlScope::generateGrid() { |
| 353 | 400 | m_vaoGrid[2].create(); |
| 354 | 401 | QOpenGLVertexArrayObject::Binder b(&m_vaoGrid[2]); |
| 355 | 402 | m_grid.bind(); |
| 356 | - m_program->enableAttributeArray(vertexLocation); | |
| 357 | - m_program->setAttributeBuffer(vertexLocation, GL_FLOAT, int(vaGrid.size() * sizeof(QVector3D)), 3); | |
| 403 | + program->enableAttributeArray(vertexLocation); | |
| 404 | + program->setAttributeBuffer(vertexLocation, GL_FLOAT, int(vaGrid.size() * sizeof(QVector3D)), 3); | |
| 358 | 405 | } |
| 359 | 406 | |
| 360 | 407 | // Border |
| ... | ... | @@ -368,6 +415,29 @@ void GlScope::generateGrid() { |
| 368 | 415 | m_grid.release(); |
| 369 | 416 | } |
| 370 | 417 | |
| 418 | +void GlScope::drawGrid() { | |
| 419 | + auto *gl = context()->versionFunctions<OPENGL_VER>(); | |
| 420 | + gl->glLineWidth(1); | |
| 421 | + | |
| 422 | + // Grid | |
| 423 | + m_vaoGrid[0].bind(); | |
| 424 | + m_program->setUniformValue(colorLocation, view->screen.grid); | |
| 425 | + gl->glDrawArrays(GL_POINTS, 0, gridDrawCounts[0]); | |
| 426 | + m_vaoGrid[0].release(); | |
| 427 | + | |
| 428 | + // Axes | |
| 429 | + m_vaoGrid[1].bind(); | |
| 430 | + m_program->setUniformValue(colorLocation, view->screen.axes); | |
| 431 | + gl->glDrawArrays(GL_LINES, 0, gridDrawCounts[1]); | |
| 432 | + m_vaoGrid[1].release(); | |
| 433 | + | |
| 434 | + // Border | |
| 435 | + m_vaoGrid[2].bind(); | |
| 436 | + m_program->setUniformValue(colorLocation, view->screen.border); | |
| 437 | + gl->glDrawArrays(GL_LINE_LOOP, 0, gridDrawCounts[2]); | |
| 438 | + m_vaoGrid[2].release(); | |
| 439 | +} | |
| 440 | + | |
| 371 | 441 | void GlScope::drawMarkers() { |
| 372 | 442 | auto *gl = context()->versionFunctions<OPENGL_VER>(); |
| 373 | 443 | QColor trColor = view->screen.markers; |
| ... | ... | @@ -375,23 +445,13 @@ void GlScope::drawMarkers() { |
| 375 | 445 | |
| 376 | 446 | m_vaoMarker.bind(); |
| 377 | 447 | |
| 378 | - for (unsigned marker = 0; marker < vaMarker.size(); ++marker) { | |
| 379 | - if (!scope->horizontal.marker_visible[marker]) continue; | |
| 380 | - vaMarker[marker] = {QVector3D((GLfloat)scope->horizontal.marker[marker], -DIVS_VOLTAGE, 0.0f), | |
| 381 | - QVector3D((GLfloat)scope->horizontal.marker[marker], DIVS_VOLTAGE, 0.0f)}; | |
| 382 | - } | |
| 383 | - | |
| 384 | - // Write coordinates to GPU | |
| 385 | - m_marker.bind(); | |
| 386 | - m_marker.write(0, vaMarker.data(), vaMarker.size() * sizeof(Line)); | |
| 387 | - | |
| 388 | 448 | // Draw all |
| 389 | 449 | gl->glLineWidth(1); |
| 390 | 450 | gl->glDrawArrays(GL_LINES, 0, (GLsizei)vaMarker.size() * 2); |
| 391 | - m_marker.release(); | |
| 392 | 451 | |
| 393 | 452 | // Draw selected |
| 394 | 453 | if (selectedMarker != NO_MARKER) { |
| 454 | + qWarning() << selectedMarker; | |
| 395 | 455 | gl->glLineWidth(3); |
| 396 | 456 | gl->glDrawArrays(GL_LINES, selectedMarker * 2, (GLsizei)2); |
| 397 | 457 | } | ... | ... |
openhantek/src/glscope.h
| ... | ... | @@ -31,10 +31,16 @@ class GlScope : public QOpenGLWidget { |
| 31 | 31 | QWidget *parent = 0); |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | + * We need at least OpenGL 3.2 with shader version 150 or | |
| 35 | + * OpenGL ES 2.0 with shader version 100. | |
| 36 | + */ | |
| 37 | + static void fixOpenGLversion(); | |
| 38 | + /** | |
| 34 | 39 | * Show new post processed data |
| 35 | 40 | * @param data |
| 36 | 41 | */ |
| 37 | 42 | void showData(PPresult* data); |
| 43 | + void markerUpdated(); | |
| 38 | 44 | |
| 39 | 45 | protected: |
| 40 | 46 | /// \brief Initializes the scope widget. |
| ... | ... | @@ -42,21 +48,23 @@ class GlScope : public QOpenGLWidget { |
| 42 | 48 | /// \param parent The parent widget. |
| 43 | 49 | GlScope(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent = 0); |
| 44 | 50 | virtual ~GlScope(); |
| 51 | + GlScope(const GlScope&) = delete; | |
| 45 | 52 | |
| 46 | 53 | /// \brief Initializes OpenGL output. |
| 47 | - void initializeGL() override; | |
| 54 | + virtual void initializeGL() override; | |
| 48 | 55 | |
| 49 | 56 | /// \brief Draw the graphs, marker and the grid. |
| 50 | - void paintGL() override; | |
| 57 | + virtual void paintGL() override; | |
| 51 | 58 | |
| 52 | 59 | /// \brief Resize the widget. |
| 53 | 60 | /// \param width The new width of the widget. |
| 54 | 61 | /// \param height The new height of the widget. |
| 55 | - void resizeGL(int width, int height) override; | |
| 62 | + virtual void resizeGL(int width, int height) override; | |
| 56 | 63 | |
| 57 | - void mousePressEvent(QMouseEvent *event) override; | |
| 58 | - void mouseMoveEvent(QMouseEvent *event) override; | |
| 59 | - void mouseReleaseEvent(QMouseEvent *event) override; | |
| 64 | + virtual void mousePressEvent(QMouseEvent *event) override; | |
| 65 | + virtual void mouseMoveEvent(QMouseEvent *event) override; | |
| 66 | + virtual void mouseReleaseEvent(QMouseEvent *event) override; | |
| 67 | + virtual void paintEvent(QPaintEvent *event) override; | |
| 60 | 68 | |
| 61 | 69 | /// \brief Draw the grid. |
| 62 | 70 | void drawGrid(); |
| ... | ... | @@ -91,13 +99,15 @@ class GlScope : public QOpenGLWidget { |
| 91 | 99 | QOpenGLBuffer m_grid; |
| 92 | 100 | QOpenGLVertexArrayObject m_vaoGrid[3]; |
| 93 | 101 | GLsizei gridDrawCounts[3]; |
| 94 | - void generateGrid(); | |
| 102 | + void generateGrid(QOpenGLShaderProgram *program); | |
| 95 | 103 | |
| 96 | 104 | // Graphs |
| 97 | 105 | std::list<Graph> m_GraphHistory; |
| 98 | 106 | unsigned currentGraphInHistory = 0; |
| 99 | 107 | |
| 100 | 108 | // OpenGL shader, matrix, var-locations |
| 109 | + bool shaderCompileSuccess = false; | |
| 110 | + QString errorMessage; | |
| 101 | 111 | std::unique_ptr<QOpenGLShaderProgram> m_program; |
| 102 | 112 | QMatrix4x4 pmvMatrix; ///< projection, view matrix |
| 103 | 113 | int colorLocation; | ... | ... |
openhantek/src/main.cpp
| ... | ... | @@ -24,6 +24,8 @@ |
| 24 | 24 | #include "usb/usbdevice.h" |
| 25 | 25 | #include "viewconstants.h" |
| 26 | 26 | |
| 27 | +#include "glscope.h" | |
| 28 | + | |
| 27 | 29 | #ifndef VERSION |
| 28 | 30 | #error "You need to run the cmake buildsystem!" |
| 29 | 31 | #endif |
| ... | ... | @@ -69,14 +71,8 @@ int main(int argc, char *argv[]) { |
| 69 | 71 | QCoreApplication::setApplicationName("OpenHantek"); |
| 70 | 72 | QCoreApplication::setApplicationVersion(VERSION); |
| 71 | 73 | |
| 72 | - QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); | |
| 73 | - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); | |
| 74 | + GlScope::fixOpenGLversion(); | |
| 74 | 75 | |
| 75 | - // Prefer full desktop OpenGL without fixed pipeline | |
| 76 | - QSurfaceFormat format; | |
| 77 | - format.setProfile(QSurfaceFormat::CoreProfile); | |
| 78 | - format.setSamples(4); // Antia-Aliasing, Multisampling | |
| 79 | - QSurfaceFormat::setDefaultFormat(format); | |
| 80 | 76 | QApplication openHantekApplication(argc, argv); |
| 81 | 77 | |
| 82 | 78 | //////// Load translations //////// |
| ... | ... | @@ -155,5 +151,10 @@ int main(int argc, char *argv[]) { |
| 155 | 151 | |
| 156 | 152 | postProcessingThread.quit(); |
| 157 | 153 | postProcessingThread.wait(10000); |
| 154 | + | |
| 155 | + if (context && device != nullptr) { | |
| 156 | + libusb_exit(context); | |
| 157 | + } | |
| 158 | + | |
| 158 | 159 | return res; |
| 159 | 160 | } | ... | ... |
openhantek/src/scopesettings.h
openhantek/src/widgets/levelslider.cpp
| ... | ... | @@ -196,17 +196,14 @@ double LevelSlider::maximum(int index) const { |
| 196 | 196 | /// \param minimum The value a slider has at the bottommost/leftmost position. |
| 197 | 197 | /// \param maximum The value a slider has at the topmost/rightmost position. |
| 198 | 198 | /// \return -1 on error, fixValue result on success. |
| 199 | -int LevelSlider::setLimits(int index, double minimum, double maximum) { | |
| 200 | - if (index < 0 || index >= this->slider.count()) return -1; | |
| 199 | +void LevelSlider::setLimits(int index, double minimum, double maximum) { | |
| 200 | + if (index < 0 || index >= this->slider.count()) return; | |
| 201 | 201 | |
| 202 | 202 | this->slider[index]->minimum = minimum; |
| 203 | 203 | this->slider[index]->maximum = maximum; |
| 204 | - int result = this->fixValue(index); | |
| 205 | - | |
| 204 | + this->fixValue(index); | |
| 206 | 205 | this->calculateRect(index); |
| 207 | 206 | this->repaint(); |
| 208 | - | |
| 209 | - return result; | |
| 210 | 207 | } |
| 211 | 208 | |
| 212 | 209 | /// \brief Return the step width of the sliders. |
| ... | ... | @@ -243,8 +240,8 @@ double LevelSlider::value(int index) const { |
| 243 | 240 | /// \param index The index of the slider whose value should be set. |
| 244 | 241 | /// \param value The new value of the slider. |
| 245 | 242 | /// \return The new value of the slider. |
| 246 | -double LevelSlider::setValue(int index, double value) { | |
| 247 | - if (index < 0 || index >= this->slider.count()) return -1; | |
| 243 | +void LevelSlider::setValue(int index, double value) { | |
| 244 | + if (index < 0 || index >= this->slider.count()) return; | |
| 248 | 245 | |
| 249 | 246 | // Apply new value |
| 250 | 247 | this->slider[index]->value = value; |
| ... | ... | @@ -254,8 +251,6 @@ double LevelSlider::setValue(int index, double value) { |
| 254 | 251 | this->repaint(); |
| 255 | 252 | |
| 256 | 253 | if (this->pressedSlider < 0) emit valueChanged(index, value); |
| 257 | - | |
| 258 | - return this->slider[index]->value; | |
| 259 | 254 | } |
| 260 | 255 | |
| 261 | 256 | /// \brief Return the direction of the sliders. |
| ... | ... | @@ -563,17 +558,14 @@ int LevelSlider::calculateWidth() { |
| 563 | 558 | /// \brief Fix the value if it's outside the limits. |
| 564 | 559 | /// \param index The index of the slider who should be fixed. |
| 565 | 560 | /// \return 0 when ok, -1 on error, 1 when increased and 2 when decreased. |
| 566 | -int LevelSlider::fixValue(int index) { | |
| 567 | - if (index < 0 || index >= this->slider.count()) return -1; | |
| 561 | +void LevelSlider::fixValue(int index) { | |
| 562 | + if (index < 0 || index >= this->slider.count()) return; | |
| 568 | 563 | |
| 569 | 564 | double lowest = qMin(this->slider[index]->minimum, this->slider[index]->maximum); |
| 570 | 565 | double highest = qMax(this->slider[index]->minimum, this->slider[index]->maximum); |
| 571 | 566 | if (this->slider[index]->value < lowest) { |
| 572 | 567 | this->slider[index]->value = lowest; |
| 573 | - return 1; | |
| 574 | 568 | } else if (this->slider[index]->value > highest) { |
| 575 | 569 | this->slider[index]->value = highest; |
| 576 | - return 2; | |
| 577 | 570 | } |
| 578 | - return 0; | |
| 579 | 571 | } | ... | ... |
openhantek/src/widgets/levelslider.h
| ... | ... | @@ -51,11 +51,11 @@ class LevelSlider : public QWidget { |
| 51 | 51 | |
| 52 | 52 | double minimum(int index) const; |
| 53 | 53 | double maximum(int index) const; |
| 54 | - int setLimits(int index, double minimum, double maximum); | |
| 54 | + void setLimits(int index, double minimum, double maximum); | |
| 55 | 55 | double step(int index) const; |
| 56 | 56 | double setStep(int index, double step); |
| 57 | 57 | double value(int index) const; |
| 58 | - double setValue(int index, double value); | |
| 58 | + void setValue(int index, double value); | |
| 59 | 59 | |
| 60 | 60 | // Parameters for all sliders |
| 61 | 61 | Qt::ArrowType direction() const; |
| ... | ... | @@ -71,7 +71,7 @@ class LevelSlider : public QWidget { |
| 71 | 71 | |
| 72 | 72 | QRect calculateRect(int sliderId); |
| 73 | 73 | int calculateWidth(); |
| 74 | - int fixValue(int index); | |
| 74 | + void fixValue(int index); | |
| 75 | 75 | |
| 76 | 76 | QList<LevelSliderParameters *> slider; ///< The parameters for each slider |
| 77 | 77 | int pressedSlider; ///< The currently pressed (moved) slider | ... | ... |