Execution policy (multithreading)

This commit is contained in:
Jakub Melka 2020-01-18 14:55:26 +01:00
parent db493db3cc
commit 1fd01c14fd
14 changed files with 266 additions and 34 deletions

View File

@ -22,6 +22,7 @@
#include "pdfutils.h"
#include "pdfpattern.h"
#include "pdfcms.h"
#include "pdfexecutionpolicy.h"
#include <QCryptographicHash>
@ -222,7 +223,7 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
};
auto range = PDFIntegerRange<unsigned int>(0, imageHeight);
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), transformPixelLine);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), transformPixelLine);
if (exception)
{

View File

@ -19,6 +19,7 @@
#include "pdfcms.h"
#include "pdfdrawspacecontroller.h"
#include "pdfprogress.h"
#include "pdfexecutionpolicy.h"
#include <QtConcurrent/QtConcurrent>
@ -411,7 +412,7 @@ void PDFAsynchronousTextLayoutCompiler::makeTextLayout()
};
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
std::for_each(std::execution::parallel_policy(), pageRange.begin(), pageRange.end(), generateTextLayout);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pageRange.begin(), pageRange.end(), generateTextLayout);
return result;
};

View File

@ -22,6 +22,7 @@
#include "pdfexception.h"
#include "pdfparser.h"
#include "pdfstreamfilters.h"
#include "pdfexecutionpolicy.h"
#include <QFile>
@ -270,7 +271,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
// Now, we are ready to scan all objects
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Reading contents of document..."));
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Unknown, occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
progressFinish();
if (m_result != Result::OK)
@ -370,7 +371,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
};
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Decrypting encrypted contents of document..."));
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), decryptEntry);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Unknown, occupiedEntries.cbegin(), occupiedEntries.cend(), decryptEntry);
progressFinish();
}
@ -478,7 +479,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
};
// Now, we are ready to scan all object streams
std::for_each(std::execution::parallel_policy(), objectStreams.cbegin(), objectStreams.cend(), processObjectStream);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Unknown, objectStreams.cbegin(), objectStreams.cend(), processObjectStream);
PDFObjectStorage storage(std::move(objects), PDFObject(xrefTable.getTrailerDictionary()), std::move(securityHandler));
return PDFDocument(std::move(storage), m_version);

View File

@ -0,0 +1,92 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#include "pdfexecutionpolicy.h"
#include <QThread>
namespace pdf
{
struct PDFExecutionPolicyHolder
{
PDFExecutionPolicy policy;
} s_execution_policy;
void PDFExecutionPolicy::setStrategy(Strategy strategy)
{
s_execution_policy.policy.m_strategy.store(strategy, std::memory_order_relaxed);
}
bool PDFExecutionPolicy::isParallelizing(Scope scope)
{
const Strategy strategy = s_execution_policy.policy.m_strategy.load(std::memory_order_relaxed);
switch (strategy)
{
case Strategy::SingleThreaded:
return false;
case Strategy::PageMultithreaded:
{
switch (scope)
{
case Scope::Page:
case Scope::Unknown:
return true; // We are parallelizing pages...
case Scope::Content:
{
// Jakub Melka: this is a bit complicated. We must count number of content streams
// being processed and if it is large enough, then do not parallelize.
const size_t threadLimit = s_execution_policy.policy.m_threadLimit.load(std::memory_order_relaxed);
const size_t contentStreamsCount = s_execution_policy.policy.m_contentStreamsCount.load(std::memory_order_seq_cst);
return contentStreamsCount < threadLimit;
}
}
break;
}
case Strategy::AlwaysMultithreaded:
return true;
}
// It should never go here...
Q_ASSERT(false);
return false;
}
void PDFExecutionPolicy::startProcessingContentStream()
{
++s_execution_policy.policy.m_contentStreamsCount;
}
void PDFExecutionPolicy::endProcessingContentStream()
{
--s_execution_policy.policy.m_contentStreamsCount;
}
PDFExecutionPolicy::PDFExecutionPolicy() :
m_contentStreamsCount(0),
m_threadLimit(QThread::idealThreadCount()),
m_strategy(Strategy::PageMultithreaded)
{
}
} // namespace pdf

View File

@ -0,0 +1,103 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFEXECUTIONPOLICY_H
#define PDFEXECUTIONPOLICY_H
#include "pdfglobal.h"
#include <atomic>
#include <execution>
namespace pdf
{
struct PDFExecutionPolicyHolder;
/// Defines thread execution policy based on settings and actual number of page content
/// streams being processed. It can regulate number of threads executed at each
/// point, where execution policy is used.
class PDFFORQTLIBSHARED_EXPORT PDFExecutionPolicy
{
public:
enum class Scope
{
Page, ///< Used, when we are processing page objects
Content, ///< Used, when we are processing objects from page content streams
Unknown ///< Unknown scope, usually tries to parallelize
};
enum class Strategy
{
SingleThreaded,
PageMultithreaded,
AlwaysMultithreaded
};
/// Sets multithreading strategy
/// \param strategy Strategy
static void setStrategy(Strategy strategy);
/// Determines, if we should parallelize for scope
/// \param scope Scope for which we want to determine exectution policy
static bool isParallelizing(Scope scope);
template<typename ForwardIt, typename UnaryFunction>
static void execute(Scope scope, ForwardIt first, ForwardIt last, UnaryFunction f)
{
if (isParallelizing(scope))
{
std::for_each(std::execution::parallel_policy(), first, last, f);
}
else
{
std::for_each(std::execution::sequenced_policy(), first, last, f);
}
}
template<typename ForwardIt, typename Comparator>
static void sort(Scope scope, ForwardIt first, ForwardIt last, Comparator f)
{
if (isParallelizing(scope))
{
std::sort(std::execution::parallel_policy(), first, last, f);
}
else
{
std::sort(std::execution::sequenced_policy(), first, last, f);
}
}
/// Starts processing content stream
static void startProcessingContentStream();
/// Ends processing content stream
static void endProcessingContentStream();
private:
friend struct PDFExecutionPolicyHolder;
explicit PDFExecutionPolicy();
std::atomic<size_t> m_contentStreamsCount;
std::atomic<size_t> m_threadLimit;
std::atomic<Strategy> m_strategy;
};
} // namespace pdf
#endif // PDFEXECUTIONPOLICY_H

View File

@ -20,6 +20,7 @@
#include "pdfexception.h"
#include "pdfimage.h"
#include "pdfpattern.h"
#include "pdfexecutionpolicy.h"
#include <QPainterPathStroker>
@ -209,6 +210,8 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
Q_ASSERT(page);
Q_ASSERT(document);
PDFExecutionPolicy::startProcessingContentStream();
QPainterPath pageRectPath;
pageRectPath.addRect(m_page->getRotatedMediaBox());
m_pageBoundingRectDeviceSpace = pagePointToDevicePointMatrix.map(pageRectPath).boundingRect();
@ -218,7 +221,7 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
PDFPageContentProcessor::~PDFPageContentProcessor()
{
PDFExecutionPolicy::endProcessingContentStream();
}
QList<PDFRenderError> PDFPageContentProcessor::processContents()

View File

@ -20,6 +20,7 @@
#include "pdfexception.h"
#include "pdfutils.h"
#include "pdfcolorspaces.h"
#include "pdfexecutionpolicy.h"
#include <QPainter>
@ -618,7 +619,7 @@ PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, c
}
};
std::for_each(std::execution::parallel_policy(), indices.cbegin(), indices.cend(), setColor);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.cbegin(), indices.cend(), setColor);
if (!functionError)
{
@ -664,7 +665,7 @@ PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, c
}
}
};
std::for_each(std::execution::parallel_policy(), indices.cbegin(), indices.cend(), validateMesh);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.cbegin(), indices.cend(), validateMesh);
if (!isMeshOK && resolution != settings.minimalMeshResolution)
{
continue;
@ -750,7 +751,7 @@ PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, c
triangles[triangleIndex1] = triangle1;
triangles[triangleIndex2] = triangle2;
};
std::for_each(std::execution::parallel_policy(), indices.cbegin(), indices.cend(), generateTriangle);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.cbegin(), indices.cend(), generateTriangle);
mesh.setTriangles(qMove(triangles));
if (!functionError)
@ -1475,7 +1476,7 @@ PDFMesh PDFFreeFormGouradTriangleShading::createMesh(const PDFMeshQualitySetting
};
PDFIntegerRange indices(size_t(0), vertexCount);
std::for_each(std::execution::parallel_policy(), indices.begin(), indices.end(), readVertex);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.begin(), indices.end(), readVertex);
mesh.setVertices(qMove(meshVertices));
vertices.front().flags = 0;
@ -1624,7 +1625,7 @@ PDFMesh PDFLatticeFormGouradTriangleShading::createMesh(const PDFMeshQualitySett
};
PDFIntegerRange indices(size_t(0), vertexCount);
std::for_each(std::execution::parallel_policy(), indices.begin(), indices.end(), readVertex);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, indices.begin(), indices.end(), readVertex);
mesh.setVertices(qMove(meshVertices));
auto getVertexIndex = [columnCount](size_t row, size_t column) -> size_t
@ -2253,7 +2254,7 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
PDFReal previousCurvature = maximalCurvature;
while (previousCurvature < curvature && !maximalCurvature.compare_exchange_weak(previousCurvature, curvature)) { }
};
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), updateCurvature);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), updateCurvature);
auto getColorForUV = [&](PDFReal u, PDFReal v)
{
@ -2364,7 +2365,7 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
QPointF rightCenter = right.getCenter();
return std::pair(leftCenter.y(), leftCenter.x()) < std::pair(rightCenter.y(), rightCenter.x());
};
std::sort(std::execution::parallel_policy(), finishedTriangles.begin(), finishedTriangles.end(), comparator);
PDFExecutionPolicy::sort(PDFExecutionPolicy::Scope::Content, finishedTriangles.begin(), finishedTriangles.end(), comparator);
std::vector<QPointF> vertices;
std::vector<PDFMesh::Triangle> triangles;

View File

@ -17,6 +17,7 @@
#include "pdftextlayout.h"
#include "pdfutils.h"
#include "pdfexecutionpolicy.h"
#include <QPainter>
@ -456,7 +457,7 @@ void PDFTextLayout::performDoLayout(PDFReal angle)
};
auto range = PDFIntegerRange<size_t>(0, characterCount);
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), findNearestCharacters);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), findNearestCharacters);
// Step 3) - detect lines
PDFUnionFindAlgorithm<size_t> textLinesUF(characterCount);
@ -847,7 +848,7 @@ PDFFindResults PDFTextLayoutStorage::find(const QString& text, Qt::CaseSensitivi
};
auto range = PDFIntegerRange<size_t>(0, m_offsets.size());
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), findImpl);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, range.begin(), range.end(), findImpl);
std::sort(results.begin(), results.end());
return results;
@ -876,7 +877,7 @@ PDFFindResults PDFTextLayoutStorage::find(const QRegularExpression& expression,
};
auto range = PDFIntegerRange<size_t>(0, m_offsets.size());
std::for_each(std::execution::parallel_policy(), range.begin(), range.end(), findImpl);
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, range.begin(), range.end(), findImpl);
std::sort(results.begin(), results.end());
return results;

View File

@ -23,6 +23,7 @@
#include "pdffont.h"
#include "pdfutils.h"
#include "pdfexception.h"
#include "pdfexecutionpolicy.h"
#include <QLocale>
#include <QPageSize>
@ -369,7 +370,7 @@ void PDFDocumentPropertiesDialog::initializeFonts(const pdf::PDFDocument* docume
};
pdf::PDFIntegerRange<pdf::PDFInteger> indices(pdf::PDFInteger(0), pageCount);
std::for_each(std::execution::parallel_policy(), indices.begin(), indices.end(), processPage);
pdf::PDFExecutionPolicy::execute(pdf::PDFExecutionPolicy::Scope::Page, indices.begin(), indices.end(), processPage);
};
m_future = QtConcurrent::run(createFontInfo);
connect(&m_futureWatcher, &QFutureWatcher<void>::finished, this, &PDFDocumentPropertiesDialog::onFontsFinished);

View File

@ -34,6 +34,7 @@
#include "pdfitemmodels.h"
#include "pdfutils.h"
#include "pdfsendmail.h"
#include "pdfexecutionpolicy.h"
#include <QSettings>
#include <QFileDialog>
@ -776,6 +777,7 @@ void PDFViewerMainWindow::onViewerSettingsChanged()
m_pdfWidget->getDrawWidgetProxy()->setPreferredMeshResolutionRatio(m_settings->getPreferredMeshResolutionRatio());
m_pdfWidget->getDrawWidgetProxy()->setMinimalMeshResolutionRatio(m_settings->getMinimalMeshResolutionRatio());
m_pdfWidget->getDrawWidgetProxy()->setColorTolerance(m_settings->getColorTolerance());
pdf::PDFExecutionPolicy::setStrategy(m_settings->getMultithreadingStrategy());
updateRenderingOptionActions();
}

View File

@ -53,6 +53,7 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti
m_settings.m_instancedFontCacheLimit = settings.value("instancedFontCacheLimit", defaultSettings.m_instancedFontCacheLimit).toInt();
m_settings.m_allowLaunchApplications = settings.value("allowLaunchApplications", defaultSettings.m_allowLaunchApplications).toBool();
m_settings.m_allowLaunchURI = settings.value("allowLaunchURI", defaultSettings.m_allowLaunchURI).toBool();
m_settings.m_multithreadingStrategy = static_cast<pdf::PDFExecutionPolicy::Strategy>(settings.value("mutlithreadingStrategy", static_cast<int>(defaultSettings.m_multithreadingStrategy)).toInt());
settings.endGroup();
settings.beginGroup("ColorManagementSystemSettings");
@ -89,6 +90,7 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.setValue("instancedFontCacheLimit", m_settings.m_instancedFontCacheLimit);
settings.setValue("allowLaunchApplications", m_settings.m_allowLaunchApplications);
settings.setValue("allowLaunchURI", m_settings.m_allowLaunchURI);
settings.setValue("mutlithreadingStrategy", static_cast<int>(m_settings.m_multithreadingStrategy));
settings.endGroup();
settings.beginGroup("ColorManagementSystemSettings");
@ -202,7 +204,8 @@ PDFViewerSettings::Settings::Settings() :
m_compiledPageCacheLimit(128 * 1024),
m_thumbnailsCacheLimit(PIXMAP_CACHE_LIMIT),
m_fontCacheLimit(pdf::DEFAULT_FONT_CACHE_LIMIT),
m_instancedFontCacheLimit(pdf::DEFAULT_REALIZED_FONT_CACHE_LIMIT)
m_instancedFontCacheLimit(pdf::DEFAULT_REALIZED_FONT_CACHE_LIMIT),
m_multithreadingStrategy(pdf::PDFExecutionPolicy::Strategy::PageMultithreaded)
{
}

View File

@ -20,6 +20,7 @@
#include "pdfrenderer.h"
#include "pdfcms.h"
#include "pdfexecutionpolicy.h"
#include <QObject>
@ -55,6 +56,7 @@ public:
pdf::PDFReal m_colorTolerance;
bool m_allowLaunchApplications;
bool m_allowLaunchURI;
pdf::PDFExecutionPolicy::Strategy m_multithreadingStrategy = pdf::PDFExecutionPolicy::Strategy::PageMultithreaded;
// Cache settings
int m_compiledPageCacheLimit;
@ -101,6 +103,8 @@ public:
const pdf::PDFCMSSettings& getColorManagementSystemSettings() const { return m_colorManagementSystemSettings; }
void setColorManagementSystemSettings(const pdf::PDFCMSSettings& settings) { m_colorManagementSystemSettings = settings; }
pdf::PDFExecutionPolicy::Strategy getMultithreadingStrategy() const { return m_settings.m_multithreadingStrategy; }
signals:
void settingsChanged();

View File

@ -59,6 +59,10 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
ui->multisampleAntialiasingSamplesCountComboBox->addItem(QString::number(i), i);
}
ui->multithreadingComboBox->addItem(tr("Single thread"), static_cast<int>(pdf::PDFExecutionPolicy::Strategy::SingleThreaded));
ui->multithreadingComboBox->addItem(tr("Multithreading (load balanced)"), static_cast<int>(pdf::PDFExecutionPolicy::Strategy::PageMultithreaded));
ui->multithreadingComboBox->addItem(tr("Multithreading (maximum threads)"), static_cast<int>(pdf::PDFExecutionPolicy::Strategy::AlwaysMultithreaded));
// Load CMS data
ui->cmsTypeComboBox->addItem(pdf::PDFCMSManager::getSystemName(pdf::PDFCMSSettings::System::Generic), int(pdf::PDFCMSSettings::System::Generic));
ui->cmsTypeComboBox->addItem(pdf::PDFCMSManager::getSystemName(pdf::PDFCMSSettings::System::LittleCMS2), int(pdf::PDFCMSSettings::System::LittleCMS2));
@ -200,6 +204,7 @@ void PDFViewerSettingsDialog::loadData()
ui->multisampleAntialiasingSamplesCountComboBox->setCurrentIndex(-1);
}
ui->prefetchPagesCheckBox->setChecked(m_settings.m_prefetchPages);
ui->multithreadingComboBox->setCurrentIndex(ui->multithreadingComboBox->findData(static_cast<int>(m_settings.m_multithreadingStrategy)));
// Rendering
ui->antialiasingCheckBox->setChecked(m_settings.m_features.testFlag(pdf::PDFRenderer::Antialiasing));
@ -397,6 +402,10 @@ void PDFViewerSettingsDialog::saveData()
{
m_cmsSettings.profileDirectory = ui->cmsProfileDirectoryEdit->text();
}
else if (sender == ui->multithreadingComboBox)
{
m_settings.m_multithreadingStrategy = static_cast<pdf::PDFExecutionPolicy::Strategy>(ui->multithreadingComboBox->currentData().toInt());
}
loadData();
}

View File

@ -26,7 +26,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>5</number>
<number>0</number>
</property>
<widget class="QWidget" name="enginePage">
<layout class="QVBoxLayout" name="enginePageLayout">
@ -57,6 +57,9 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="renderingEngineComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="multisampleAntialiasingLabel">
<property name="text">
@ -64,6 +67,16 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="multisampleAntialiasingSamplesCountComboBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="prefetchPagesLabel">
<property name="text">
<string>Prefetch pages</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="multisampleAntiailasingSamplesLabel">
<property name="text">
@ -71,11 +84,12 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="multisampleAntialiasingSamplesCountComboBox"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="renderingEngineComboBox"/>
<item row="3" column="1">
<widget class="QCheckBox" name="prefetchPagesCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="renderingEngineLabel">
@ -84,26 +98,22 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="prefetchPagesLabel">
<item row="4" column="0">
<widget class="QLabel" name="multithreadingLabel">
<property name="text">
<string>Prefetch pages</string>
<string>Multithreading strategy</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="prefetchPagesCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
<item row="4" column="1">
<widget class="QComboBox" name="multithreadingComboBox"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="engineInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select rendering method according to your needs. &lt;span style=&quot; font-weight:600;&quot;&gt;Software rendering&lt;/span&gt; is much slower than hardware accelerated rendering using &lt;span style=&quot; font-weight:600;&quot;&gt;OpenGL rendering&lt;/span&gt;, but it works when OpenGL is not available at your platform. OpenGL rendering is selected as default and is recommended.&lt;/p&gt;&lt;p&gt;OpenGL rendering uses&lt;span style=&quot; font-weight:600;&quot;&gt; multisample antialiasing (MSAA)&lt;/span&gt;, which provides good quality antialiasing. You can turn this feature on or off, but without antialiasing, bad quality image can occur. Samples count affect how much samples per pixel are considered to determine pixel color. It can be a value 1, 2, 4, 8, and 16. Most modern GPUs support at least value 8. Lower this value, if your GPU doesn't support the desired sample count.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Prefetch pages &lt;/span&gt;prefetches (pre-renders) pages next to currently viewed pages, to avoid flickering during scrolling. Prefetched pages are stored in the page cache.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select rendering method according to your needs. &lt;span style=&quot; font-weight:600;&quot;&gt;Software rendering&lt;/span&gt; is much slower than hardware accelerated rendering using &lt;span style=&quot; font-weight:600;&quot;&gt;OpenGL rendering&lt;/span&gt;, but it works when OpenGL is not available at your platform. OpenGL rendering is selected as default and is recommended.&lt;/p&gt;&lt;p&gt;OpenGL rendering uses&lt;span style=&quot; font-weight:600;&quot;&gt; multisample antialiasing (MSAA)&lt;/span&gt;, which provides good quality antialiasing. You can turn this feature on or off, but without antialiasing, bad quality image can occur. Samples count affect how much samples per pixel are considered to determine pixel color. It can be a value 1, 2, 4, 8, and 16. Most modern GPUs support at least value 8. Lower this value, if your GPU doesn't support the desired sample count.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Prefetch pages &lt;/span&gt;prefetches (pre-renders) pages next to currently viewed pages, to avoid flickering during scrolling. Prefetched pages are stored in the page cache.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Multithreading strategy &lt;/span&gt;defines how program will use CPU cores. Engine can use multiple cores. Strategy defines, how engine will use these cores. &lt;span style=&quot; font-weight:600;&quot;&gt;Single thread&lt;/span&gt; strategy uses only one CPU core for rendering page, and for some operations, they aren't parallelized, at the cost of more time needed for operation to be finished. But still, each page will use its own thread to be compiled/drawn. On the other side, there are two multithreading strategies, former is load balanced, latter uses maximum threads. Load balanced strategy tries to optimize number of threads to fit CPU cores, while maximum threads strategy will spawn as much threads as possible to process operations, which can be sometimes unoptimal.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>