From 6224f226b21625674aa451a6e8f8bb92c0461a49 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sat, 22 Apr 2023 13:17:42 +0200 Subject: [PATCH] Issue #46: Program does not launch/freezes on Windows 10/Windows Server 2022 (x64) --- Pdf4QtLib/sources/pdfdrawwidget.cpp | 21 +++++- Pdf4QtLib/sources/pdfdrawwidget.h | 2 + Pdf4QtLib/sources/pdfrenderer.cpp | 81 ++++++++++++++++++++++++ Pdf4QtLib/sources/pdfrenderer.h | 30 ++++++++- Pdf4QtViewer/pdfprogramcontroller.cpp | 14 ++++ Pdf4QtViewer/pdfprogramcontroller.h | 2 + Pdf4QtViewer/pdfviewermainwindow.cpp | 1 + Pdf4QtViewer/pdfviewermainwindowlite.cpp | 3 +- 8 files changed, 148 insertions(+), 6 deletions(-) diff --git a/Pdf4QtLib/sources/pdfdrawwidget.cpp b/Pdf4QtLib/sources/pdfdrawwidget.cpp index 7e589d7..e69b6ec 100644 --- a/Pdf4QtLib/sources/pdfdrawwidget.cpp +++ b/Pdf4QtLib/sources/pdfdrawwidget.cpp @@ -44,7 +44,7 @@ PDFWidget::PDFWidget(const PDFCMSManager* cmsManager, RendererEngine engine, int m_verticalScrollBar(nullptr), m_proxy(nullptr) { - m_drawWidget = createDrawWidget(engine, samplesCount); + m_drawWidget = createDrawWidget(getEffectiveRenderer(engine), samplesCount); m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this); m_verticalScrollBar = new QScrollBar(Qt::Vertical, this); @@ -90,6 +90,8 @@ void PDFWidget::setDocument(const PDFModifiedDocument& document) void PDFWidget::updateRenderer(RendererEngine engine, int samplesCount) { + engine = getEffectiveRenderer(engine); + PDFOpenGLDrawWidget* openglDrawWidget = qobject_cast(m_drawWidget->getWidget()); PDFDrawWidget* softwareDrawWidget = qobject_cast(m_drawWidget->getWidget()); @@ -207,6 +209,16 @@ void PDFWidget::addInputInterface(IDrawWidgetInputInterface* inputInterface) } } +RendererEngine PDFWidget::getEffectiveRenderer(RendererEngine rendererEngine) +{ + if (rendererEngine == RendererEngine::OpenGL && !pdf::PDFRendererInfo::isHardwareAccelerationSupported()) + { + return RendererEngine::Software; + } + + return rendererEngine; +} + PDFFormManager* PDFWidget::getFormManager() const { return m_formManager; @@ -591,8 +603,11 @@ void PDFOpenGLDrawWidget::initializeGL() void PDFOpenGLDrawWidget::paintGL() { - QPainter painter(this); - getPDFWidget()->getDrawWidgetProxy()->draw(&painter, this->rect()); + if (this->isValid()) + { + QPainter painter(this); + getPDFWidget()->getDrawWidgetProxy()->draw(&painter, this->rect()); + } } PDFDrawWidget::PDFDrawWidget(PDFWidget* widget, QWidget* parent) : diff --git a/Pdf4QtLib/sources/pdfdrawwidget.h b/Pdf4QtLib/sources/pdfdrawwidget.h index 998e14a..ba7e47e 100644 --- a/Pdf4QtLib/sources/pdfdrawwidget.h +++ b/Pdf4QtLib/sources/pdfdrawwidget.h @@ -106,6 +106,8 @@ signals: void pageRenderingErrorsChanged(pdf::PDFInteger pageIndex, int errorsCount); private: + RendererEngine getEffectiveRenderer(RendererEngine rendererEngine); + void updateRendererImpl(); void onRenderingError(PDFInteger pageIndex, const QList& errors); void onPageImageChanged(bool all, const std::vector& pages); diff --git a/Pdf4QtLib/sources/pdfrenderer.cpp b/Pdf4QtLib/sources/pdfrenderer.cpp index 3ea3d3a..c19918e 100644 --- a/Pdf4QtLib/sources/pdfrenderer.cpp +++ b/Pdf4QtLib/sources/pdfrenderer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace pdf { @@ -204,6 +205,12 @@ PDFRasterizer::~PDFRasterizer() void PDFRasterizer::reset(bool useOpenGL, const QSurfaceFormat& surfaceFormat) { + if (!PDFRendererInfo::isHardwareAccelerationSupported()) + { + m_features.setFlag(FailedOpenGL, true); + m_features.setFlag(ValidOpenGL, false); + } + if (useOpenGL != m_features.testFlag(UseOpenGL) || surfaceFormat != m_surfaceFormat) { // In either case, we must reset OpenGL @@ -927,4 +934,78 @@ PDFRasterizerPool::PDFRasterizerPool(const PDFDocument* document, } } +PDFCachedItem PDFRendererInfo::s_info; + +const PDFRendererInfo::Info& PDFRendererInfo::getHardwareAccelerationSupportedInfo() +{ + auto getInfo = []() + { + Info info; + + QOffscreenSurface surface; + surface.create(); + + if (!surface.isValid()) + { + info.renderer = PDFTranslationContext::tr("Unknown Device"); + info.version = PDFTranslationContext::tr("?.?"); + info.vendor = PDFTranslationContext::tr("Generic"); + return info; + } + + QOpenGLContext context; + + if (!context.create()) + { + info.renderer = PDFTranslationContext::tr("Unknown Device"); + info.version = PDFTranslationContext::tr("?.?"); + info.vendor = PDFTranslationContext::tr("Generic"); + surface.destroy(); + return info; + } + + if (!context.makeCurrent(&surface)) + { + info.renderer = PDFTranslationContext::tr("Unknown Device"); + info.version = PDFTranslationContext::tr("?.?"); + info.vendor = PDFTranslationContext::tr("Generic"); + surface.destroy(); + return info; + } + + const char* versionStr = reinterpret_cast(context.functions()->glGetString(GL_VERSION)); + const char* vendorStr = reinterpret_cast(context.functions()->glGetString(GL_VENDOR)); + const char* rendererStr = reinterpret_cast(context.functions()->glGetString(GL_RENDERER)); + + QString versionString = QString::fromLocal8Bit(versionStr, std::strlen(versionStr)); + QString vendorString = QString::fromLocal8Bit(vendorStr, std::strlen(vendorStr)); + QString rendererString = QString::fromLocal8Bit(rendererStr, std::strlen(rendererStr)); + + context.doneCurrent(); + surface.destroy(); + + info.vendor = vendorString; + info.renderer = rendererString; + info.version = versionString; + + QStringList versionStrSplitted = versionString.split('.', Qt::KeepEmptyParts); + + if (versionStrSplitted.size() >= 2) + { + info.majorOpenGLVersion = versionStrSplitted[0].toInt(); + info.minorOpenGLVersion = versionStrSplitted[1].toInt(); + } + + return info; + }; + + return s_info.get(getInfo); +} + +bool PDFRendererInfo::isHardwareAccelerationSupported() +{ + const Info& info = getHardwareAccelerationSupportedInfo(); + return std::make_pair(info.majorOpenGLVersion, info.minorOpenGLVersion) >= std::make_pair(REQUIRED_OPENGL_MAJOR_VERSION, REQUIRED_OPENGL_MINOR_VERSION); +} + } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfrenderer.h b/Pdf4QtLib/sources/pdfrenderer.h index bad2f1f..268392d 100644 --- a/Pdf4QtLib/sources/pdfrenderer.h +++ b/Pdf4QtLib/sources/pdfrenderer.h @@ -22,6 +22,7 @@ #include "pdfexception.h" #include "pdfoperationcontrol.h" #include "pdfmeshqualitysettings.h" +#include "pdfutils.h" #include #include @@ -44,6 +45,30 @@ class PDFPrecompiledPage; class PDFAnnotationManager; class PDFOptionalContentActivity; +class PDF4QTLIBSHARED_EXPORT PDFRendererInfo +{ +public: + PDFRendererInfo() = delete; + + struct Info + { + QString vendor; + QString renderer; + QString version; + int majorOpenGLVersion = 0; + int minorOpenGLVersion = 0; + }; + + static const Info& getHardwareAccelerationSupportedInfo(); + static bool isHardwareAccelerationSupported(); + + static constexpr int REQUIRED_OPENGL_MAJOR_VERSION = 3; + static constexpr int REQUIRED_OPENGL_MINOR_VERSION = 2; + +private: + static PDFCachedItem s_info; +}; + /// Renders the PDF page on the painter, or onto an image. class PDF4QTLIBSHARED_EXPORT PDFRenderer { @@ -144,8 +169,9 @@ public: /// Resets the renderer. This function must be called from main GUI thread, /// it cannot be called from deferred threads, because it can create hidden - /// window (offscreen surface). - /// \param useOpenGL Use OpenGL for rendering + /// window (offscreen surface). If hardware renderer is required, and + /// none is available, then software renderer is used. + /// \param useOpenGL Use OpenGL for rendering (ignored if not available) /// \param surfaceFormat Surface format to render void reset(bool useOpenGL, const QSurfaceFormat& surfaceFormat); diff --git a/Pdf4QtViewer/pdfprogramcontroller.cpp b/Pdf4QtViewer/pdfprogramcontroller.cpp index 837aac0..6b88363 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.cpp +++ b/Pdf4QtViewer/pdfprogramcontroller.cpp @@ -2028,6 +2028,20 @@ void PDFProgramController::resetSettings() } } +void PDFProgramController::checkHardwareOpenGLAvailability() +{ + if (m_settings->getRendererEngine() == pdf::RendererEngine::OpenGL && + !pdf::PDFRendererInfo::isHardwareAccelerationSupported()) + { + pdf::PDFRendererInfo::Info info = pdf::PDFRendererInfo::getHardwareAccelerationSupportedInfo(); + QMessageBox::warning(m_mainWindow, tr("Warning"), + tr("Hardware acceleration is not supported on this device. " + "OpenGL version at least 3.2 is required. Software rendering is used instead. " + "Available OpenGL is %1 using %2. You can turn off hardware acceleration " + "in 'Tools' menu using 'Options' item to stop displaying this message.").arg(info.version, info.renderer)); + } +} + void PDFProgramController::onActionOptionsTriggered() { PDFViewerSettingsDialog::OtherSettings otherSettings; diff --git a/Pdf4QtViewer/pdfprogramcontroller.h b/Pdf4QtViewer/pdfprogramcontroller.h index a664877..b74b6e0 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.h +++ b/Pdf4QtViewer/pdfprogramcontroller.h @@ -276,6 +276,8 @@ public: void writeSettings(); void resetSettings(); + void checkHardwareOpenGLAvailability(); + void performPrint(); void performSave(); void performSaveAs(); diff --git a/Pdf4QtViewer/pdfviewermainwindow.cpp b/Pdf4QtViewer/pdfviewermainwindow.cpp index 21432d0..12a8a5d 100644 --- a/Pdf4QtViewer/pdfviewermainwindow.cpp +++ b/Pdf4QtViewer/pdfviewermainwindow.cpp @@ -523,6 +523,7 @@ void PDFViewerMainWindow::showEvent(QShowEvent* event) { QMainWindow::showEvent(event); m_progressTaskbarIndicator->setWindow(windowHandle()); + QTimer::singleShot(0, this, [this] { m_programController->checkHardwareOpenGLAvailability(); }); } void PDFViewerMainWindow::dragEnterEvent(QDragEnterEvent* event) diff --git a/Pdf4QtViewer/pdfviewermainwindowlite.cpp b/Pdf4QtViewer/pdfviewermainwindowlite.cpp index b33625b..f938dfc 100644 --- a/Pdf4QtViewer/pdfviewermainwindowlite.cpp +++ b/Pdf4QtViewer/pdfviewermainwindowlite.cpp @@ -412,8 +412,9 @@ void PDFViewerMainWindowLite::closeEvent(QCloseEvent* event) void PDFViewerMainWindowLite::showEvent(QShowEvent* event) { - Q_UNUSED(event); + QMainWindow::showEvent(event); m_progressTaskbarIndicator->setWindow(windowHandle()); + QTimer::singleShot(0, this, [this] { m_programController->checkHardwareOpenGLAvailability(); }); } void PDFViewerMainWindowLite::dragEnterEvent(QDragEnterEvent* event)