summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2019-09-03 17:36:07 +0300
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2019-09-12 06:43:23 +0300
commit82574e7d6317898600958c356053cfdaa7321c32 (patch)
treed4d099d426094292a01e81cbb3f389a20a961cfc
parent0d6360909793aab11c66f4b8f7001f71cbacfee7 (diff)
Implement shader caching
Currently loaded shaders can now cached with an API call in either source code or binary format. It is necessary to do it that way as the shader cache can only be created in the target hardware. Task-number: QT3DS-3914 Change-Id: I094e8624943904c311ada2259a84b8c183f792f5 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
-rw-r--r--src/api/studio3d/q3dscommandqueue.cpp49
-rw-r--r--src/api/studio3d/q3dscommandqueue_p.h59
-rw-r--r--src/api/studio3d/q3dspresentation.cpp259
-rw-r--r--src/api/studio3d/q3dspresentation.h8
-rw-r--r--src/api/studio3d/q3dspresentation_p.h10
-rw-r--r--src/api/studio3d/q3dssurfaceviewer.cpp1
-rw-r--r--src/api/studio3dqml/q3dsrenderer.cpp26
-rw-r--r--src/api/studio3dqml/q3dsruntimeInitializerthread.cpp5
-rw-r--r--src/api/studio3dqml/q3dsruntimeInitializerthread_p.h3
-rw-r--r--src/api/studio3dqml/q3dsstudio3d.cpp25
-rw-r--r--src/engine/Qt3DSRenderRuntimeBinding.cpp2
-rw-r--r--src/engine/Qt3DSRenderRuntimeBindingImpl.h2
-rw-r--r--src/engine/Qt3DSRenderRuntimeBindingImplRenderer.cpp10
-rw-r--r--src/engine/Qt3DSRuntimeView.cpp8
-rw-r--r--src/engine/Qt3DSRuntimeView.h5
-rw-r--r--src/render/Qt3DSRenderContext.cpp20
-rw-r--r--src/render/Qt3DSRenderContext.h10
-rw-r--r--src/render/Qt3DSRenderShaderProgram.cpp102
-rw-r--r--src/render/Qt3DSRenderShaderProgram.h11
-rw-r--r--src/render/backends/Qt3DSRenderBackend.h12
-rw-r--r--src/render/backends/gl/Q3DSRenderBackendGLES2.cpp3
-rw-r--r--src/render/backends/gl/Qt3DSRenderBackendGL3.cpp1
-rw-r--r--src/render/backends/gl/Qt3DSRenderBackendGL4.cpp1
-rw-r--r--src/render/backends/gl/Qt3DSRenderBackendGLBase.cpp34
-rw-r--r--src/render/backends/gl/Qt3DSRenderBackendGLBase.h8
-rw-r--r--src/render/backends/software/Qt3DSRenderBackendNULL.cpp6
-rw-r--r--src/runtime/Qt3DSApplication.cpp12
-rw-r--r--src/runtime/Qt3DSApplication.h1
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCache.cpp572
-rw-r--r--src/runtimerender/Qt3DSRenderShaderCache.h23
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp2
-rw-r--r--src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp144
-rw-r--r--src/viewer/Qt3DSViewerApp.cpp12
-rw-r--r--src/viewer/Qt3DSViewerApp.h3
-rw-r--r--tools/viewer/main.cpp26
35 files changed, 896 insertions, 579 deletions
diff --git a/src/api/studio3d/q3dscommandqueue.cpp b/src/api/studio3d/q3dscommandqueue.cpp
index 8ec07ee..06bd73f 100644
--- a/src/api/studio3d/q3dscommandqueue.cpp
+++ b/src/api/studio3d/q3dscommandqueue.cpp
@@ -46,27 +46,6 @@ QString ElementCommand::toString() const
}
CommandQueue::CommandQueue()
- : m_visibleChanged(false)
- , m_scaleModeChanged(false)
- , m_stereoModeChanged(false)
- , m_stereoEyeSeparationChanged(false)
- , m_shadeModeChanged(false)
- , m_showRenderStatsChanged(false)
- , m_matteColorChanged(false)
- , m_sourceChanged(false)
- , m_variantListChanged(false)
- , m_globalAnimationTimeChanged(false)
- , m_delayedLoadingChanged(false)
- , m_visible(false)
- , m_scaleMode(Q3DSViewerSettings::ScaleModeCenter)
- , m_stereoMode(Q3DSViewerSettings::StereoModeMono)
- , m_stereoEyeSeparation(0.4)
- , m_shadeMode(Q3DSViewerSettings::ShadeModeShaded)
- , m_showRenderStats(false)
- , m_matteColor(Qt::black)
- , m_delayedLoading(false)
- , m_matteEnabled(false)
- , m_size(0)
{
qRegisterMetaType<CommandType>();
}
@@ -212,6 +191,25 @@ ElementCommand &CommandQueue::queueCommand(const QString &elementPath, CommandTy
return cmd;
}
+ElementCommand &CommandQueue::queueCommand(CommandType commandType)
+{
+ ElementCommand &cmd = nextFreeCommand();
+
+ cmd.m_commandType = commandType;
+
+ return cmd;
+}
+
+ElementCommand &CommandQueue::queueCommand(CommandType commandType, bool value)
+{
+ ElementCommand &cmd = nextFreeCommand();
+
+ cmd.m_commandType = commandType;
+ cmd.m_boolValue = value;
+
+ return cmd;
+}
+
void CommandQueue::copyCommands(CommandQueue &fromQueue)
{
m_visibleChanged = m_visibleChanged || fromQueue.m_visibleChanged;
@@ -228,6 +226,7 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue)
= m_globalAnimationTimeChanged || fromQueue.m_globalAnimationTimeChanged;
m_delayedLoadingChanged = m_delayedLoadingChanged || fromQueue.m_delayedLoadingChanged;
m_matteEnabledChanged = m_matteEnabledChanged || fromQueue.m_matteEnabledChanged;
+ m_shaderCacheFileChanged = m_shaderCacheFileChanged || fromQueue.m_shaderCacheFileChanged;
if (fromQueue.m_visibleChanged)
m_visible = fromQueue.m_visible;
@@ -253,6 +252,8 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue)
m_delayedLoading = fromQueue.m_delayedLoading;
if (fromQueue.m_matteEnabledChanged)
m_matteEnabled = fromQueue.m_matteEnabled;
+ if (fromQueue.m_shaderCacheFileChanged)
+ m_shaderCacheFile = fromQueue.m_shaderCacheFile;
// Pending queue may be synchronized multiple times between queue processing, so let's append
// to the existing queue rather than clearing it.
@@ -313,9 +314,12 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue)
case CommandType_RequestSlideInfo:
case CommandType_UnloadSlide:
case CommandType_PreloadSlide:
+ queueCommand(source.m_elementPath, source.m_commandType);
+ break;
case CommandType_RequestDataInputs:
case CommandType_RequestDataOutputs:
- queueCommand(source.m_elementPath, source.m_commandType);
+ case CommandType_RequestExportShaderCache:
+ queueCommand(source.m_commandType, source.m_boolValue);
break;
default:
queueCommand(QString(), CommandType_Invalid, false);
@@ -339,6 +343,7 @@ void CommandQueue::clear(bool deleteCommandData)
m_globalAnimationTimeChanged = false;
m_delayedLoadingChanged = false;
m_matteEnabledChanged = false;
+ m_shaderCacheFileChanged = false;
if (deleteCommandData) {
for (int i = 0; i < m_size; ++i) {
diff --git a/src/api/studio3d/q3dscommandqueue_p.h b/src/api/studio3d/q3dscommandqueue_p.h
index de1f2b1..2f98ed6 100644
--- a/src/api/studio3d/q3dscommandqueue_p.h
+++ b/src/api/studio3d/q3dscommandqueue_p.h
@@ -75,13 +75,14 @@ enum CommandType {
CommandType_DeleteMaterials,
CommandType_CreateMeshes,
CommandType_DeleteMeshes,
+ CommandType_PreloadSlide,
+ CommandType_UnloadSlide,
// Requests
CommandType_RequestSlideInfo,
CommandType_RequestDataInputs,
- CommandType_PreloadSlide,
- CommandType_UnloadSlide,
- CommandType_RequestDataOutputs
+ CommandType_RequestDataOutputs,
+ CommandType_RequestExportShaderCache
};
class Q_STUDIO3D_EXPORT ElementCommand
@@ -132,34 +133,38 @@ public:
ElementCommand &queueCommand(const QString &elementPath, CommandType commandType);
ElementCommand &queueCommand(const QString &elementPath, CommandType commandType,
void *commandData);
+ ElementCommand &queueCommand(CommandType commandType);
+ ElementCommand &queueCommand(CommandType commandType, bool value);
void copyCommands(CommandQueue &fromQueue);
- bool m_visibleChanged;
- bool m_scaleModeChanged;
- bool m_stereoModeChanged;
- bool m_stereoEyeSeparationChanged;
- bool m_shadeModeChanged;
- bool m_showRenderStatsChanged;
- bool m_matteColorChanged;
- bool m_sourceChanged;
- bool m_variantListChanged;
- bool m_globalAnimationTimeChanged;
- bool m_delayedLoadingChanged;
- bool m_matteEnabledChanged;
-
- bool m_visible;
- Q3DSViewerSettings::ScaleMode m_scaleMode;
- Q3DSViewerSettings::StereoMode m_stereoMode;
- double m_stereoEyeSeparation;
- Q3DSViewerSettings::ShadeMode m_shadeMode;
- bool m_showRenderStats;
- QColor m_matteColor;
+ bool m_visibleChanged = false;
+ bool m_scaleModeChanged = false;
+ bool m_stereoModeChanged = false;
+ bool m_stereoEyeSeparationChanged = false;
+ bool m_shadeModeChanged = false;
+ bool m_showRenderStatsChanged = false;
+ bool m_matteColorChanged = false;
+ bool m_sourceChanged = false;
+ bool m_variantListChanged = false;
+ bool m_globalAnimationTimeChanged = false;
+ bool m_delayedLoadingChanged = false;
+ bool m_matteEnabledChanged = false;
+ bool m_shaderCacheFileChanged = false;
+
+ bool m_visible = false;
+ Q3DSViewerSettings::ScaleMode m_scaleMode = Q3DSViewerSettings::ScaleModeCenter;
+ Q3DSViewerSettings::StereoMode m_stereoMode = Q3DSViewerSettings::StereoModeMono;
+ double m_stereoEyeSeparation = 0.4;
+ Q3DSViewerSettings::ShadeMode m_shadeMode = Q3DSViewerSettings::ShadeModeShaded;
+ bool m_showRenderStats = false;
+ QColor m_matteColor = QColor(Qt::black);
QUrl m_source;
QStringList m_variantList;
- qint64 m_globalAnimationTime;
- bool m_delayedLoading;
- bool m_matteEnabled;
+ qint64 m_globalAnimationTime = 0;
+ bool m_delayedLoading = false;
+ bool m_matteEnabled = false;
+ QUrl m_shaderCacheFile;
void clear(bool deleteCommandData);
int size() const { return m_size; }
@@ -170,7 +175,7 @@ private:
ElementCommand &nextFreeCommand();
CommandList m_elementCommands;
- int m_size;
+ int m_size = 0;
};
QT_END_NAMESPACE
diff --git a/src/api/studio3d/q3dspresentation.cpp b/src/api/studio3d/q3dspresentation.cpp
index beb0394..e782cfe 100644
--- a/src/api/studio3d/q3dspresentation.cpp
+++ b/src/api/studio3d/q3dspresentation.cpp
@@ -34,10 +34,15 @@
#include "q3dsdatainput_p.h"
#include "q3dsdataoutput_p.h"
#include "q3dsgeometry_p.h"
+#include "studioutils_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qsettings.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qsavefile.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
#include <QtGui/qevent.h>
QT_BEGIN_NAMESPACE
@@ -607,6 +612,161 @@ void Q3DSPresentation::unloadSlide(const QString &elementPath)
}
/*!
+ \qmlmethod Presentation::exportShaderCache(url shaderCacheFile, bool binaryShaders)
+
+ Writes the shaders currently in use to the file specified by \a shaderCacheFile URL.
+ If \a binaryShaders property is \c{true}, precompiled shaders are exported. Otherwise,
+ the shader source code is exported.
+
+ Exporting shader cache is an asynchronous operation.
+ The shaderCacheExported signal is emitted when the export is complete.
+
+ Exporting shader cache should be done at the point of application execution where all the
+ shaders that should be initialized at application startup have been initialized.
+
+ \note Exporting shader cache is only supported if no shaders have been originally loaded
+ from a shader cache. Specifying no shader cache file or an empty or invalid shader cache file
+ with shaderCacheFile property allows shader generation.
+
+ \sa shaderCacheFile, shaderCacheExported
+ */
+/*!
+ Writes the shaders currently in use to the file specified by \a shaderCacheFile URL.
+ If \a binaryShaders property is \c{true}, precompiled shaders are exported. Otherwise,
+ the shader source code is exported.
+
+ The shaderCacheExported signal is emitted when the export is complete.
+
+ Exporting shader cache should be done at the point of application execution where all the
+ shaders that should be initialized at application startup have been initialized.
+
+ \note Exporting shader cache is only supported if no shaders have been originally loaded
+ from a shader cache. Specifying no shader cache file or an empty or invalid shader cache file
+ with shaderCacheFile property allows shader generation.
+
+ \sa shaderCacheFile, shaderCacheExported
+ */
+void Q3DSPresentation::exportShaderCache(const QUrl &shaderCacheFile, bool binaryShaders)
+{
+ d_ptr->exportShaderCache(binaryShaders, false);
+ if (d_ptr->m_commandQueue)
+ d_ptr->m_shaderCacheWritePending = shaderCacheFile;
+ else
+ d_ptr->writeShaderCache(shaderCacheFile);
+}
+
+/*!
+ \qmlmethod Presentation::exportShaderCache(bool binaryShaders)
+
+ Exports the shaders currently in use and dumps the resulting cache encoded with base64 into
+ stderr. This function is provided as a means to extract the shader cache from environments
+ without a writable disk. The base64 output needs to be converted back to binary
+ representation to be usable as a shader cache file. The Qt 3D Studio Viewer provides
+ a command line parameter \c --convert-shader-cache to do this conversion.
+
+ If \a binaryShaders property is \c{true}, precompiled shaders are exported.
+ Otherwise, the shader source code is exported.
+
+ Exporting shader cache is an asynchronous operation.
+ The shaderCacheExported signal is emitted when the export is complete.
+
+ Exporting shader cache should be done at the point of application execution where all the
+ shaders that should be initialized at application startup have been initialized.
+
+ \note Exporting shader cache is only supported if no shaders have been originally loaded
+ from a shader cache. Specifying no shader cache file or an empty or invalid shader cache file
+ with shaderCacheFile property allows shader generation.
+
+ \sa shaderCacheFile, shaderCacheExported
+ */
+/*!
+ Exports the shaders currently in use and dumps the resulting cache encoded with base64 into
+ stderr. This function is provided as a means to extract the shader cache from environments
+ without a writable disk. The base64 output needs to be converted back to binary
+ representation to be usable as a shader cache file. The Qt 3D Studio Viewer provides
+ a command line parameter \c --convert-shader-cache to do this conversion.
+
+ If \a binaryShaders property is \c{true}, precompiled shaders are exported.
+ Otherwise, the shader source code is exported.
+
+ The shaderCacheExported signal is emitted when the export is complete.
+
+ Exporting shader cache should be done at the point of application execution where all the
+ shaders that should be initialized at application startup have been initialized.
+
+ \note Exporting shader cache is only supported if no shaders have been originally loaded
+ from a shader cache. Specifying no shader cache file or an empty or invalid shader cache file
+ with shaderCacheFile property allows shader generation.
+
+ \sa shaderCacheFile, shaderCacheExported
+ */
+void Q3DSPresentation::exportShaderCache(bool binaryShaders)
+{
+ d_ptr->exportShaderCache(binaryShaders, true);
+}
+
+/*!
+ \qmlproperty url Presentation::shaderCacheFile
+
+ Specifies the shader cache file to be used for initial shader initialization.
+ This property value must be set before the presentation is shown.
+ Using cached shaders improves presentation initialization speed.
+
+ If this property is not set, all shaders are generated normally.
+
+ If this property points to a valid shader cache file, new shader cache generation is not
+ supported.
+
+ The default value is an empty url.
+
+ \sa exportShaderCache(), shaderCacheExport
+ */
+/*!
+ Specifies the shader cache file to be used for initial shader initialization.
+ This property value must be set before the presentation is shown.
+ Using cached shaders improves presentation initialization speed.
+
+ If this property is not set, all shaders are generated normally.
+
+ If this property points to a valid shader cache file, new shader cache generation is not
+ supported.
+
+ The default value is an empty url.
+
+ \sa exportShaderCache(), shaderCacheExport
+ */
+QUrl Q3DSPresentation::shaderCacheFile() const
+{
+ return d_ptr->m_shaderCacheFile;
+}
+
+void Q3DSPresentation::setShaderCacheFile(const QUrl &fileName)
+{
+ if (d_ptr->m_shaderCacheFile != fileName) {
+ d_ptr->setShaderCacheFile(fileName);
+ Q_EMIT shaderCacheFileChanged(fileName);
+ }
+}
+
+/*!
+ \qmlsignal Presentation::shaderCacheExported(bool success)
+
+ Emitted when a shader cache export is completed. The parameter \a success indicates whether
+ or not the export was successful.
+
+ \sa shaderCacheExport(), shaderCacheFile
+ */
+
+/*!
+ \fn Q3DSPresentation::shaderCacheExported(bool success)
+
+ Emitted when a shader cache export is completed. The parameter \a success indicates whether
+ or not the export was successful.
+
+ \sa shaderCacheExport(), shaderCacheFile
+ */
+
+/*!
This function is for backwards compatibility. We recommend using \l{DataInput}s to control
slide changes. \l{DataInput} provides stronger contract between the design and
code as it avoids use of elementPath (a reference to design's internal structure).
@@ -1626,11 +1786,12 @@ void Q3DSPresentationPrivate::setCommandQueue(CommandQueue *queue)
if (m_commandQueue) {
setDelayedLoading(m_delayedLoading);
setVariantList(m_variantList);
+ setShaderCacheFile(m_shaderCacheFile);
// Queue a request ASAP for datainputs and outputs defined in UIA file so that
// getDataInputs has up-to-date info at the earliest and that data outputs
// connect from source to destination
- m_commandQueue->queueCommand({}, CommandType_RequestDataInputs);
- m_commandQueue->queueCommand({}, CommandType_RequestDataOutputs);
+ m_commandQueue->queueCommand(CommandType_RequestDataInputs);
+ m_commandQueue->queueCommand(CommandType_RequestDataOutputs);
setSource(m_source);
}
}
@@ -1655,12 +1816,21 @@ void Q3DSPresentationPrivate::setDataInputDirty(const QString &name, bool dirty)
m_dataInputs[name]->d_ptr->setDirty(dirty);
}
+void Q3DSPresentationPrivate::setShaderCacheFile(const QUrl &fileName)
+{
+ m_shaderCacheFile = fileName;
+ if (m_commandQueue) {
+ m_commandQueue->m_shaderCacheFile = fileName;
+ m_commandQueue->m_shaderCacheFileChanged = true;
+ }
+}
+
void Q3DSPresentationPrivate::requestResponseHandler(CommandType commandType, void *requestData)
{
+ QVariantList *response = reinterpret_cast<QVariantList *>(requestData);
+
switch (commandType) {
case CommandType_RequestDataInputs: {
- QVariantList *response = reinterpret_cast<QVariantList *>(requestData);
-
for (int i = 0; i < response->size(); ++i) {
// Check and append to QML-side list if the (UIA) presentation has additional datainputs
// that are not explicitly defined in QML code.
@@ -1680,27 +1850,100 @@ void Q3DSPresentationPrivate::requestResponseHandler(CommandType commandType, vo
m_dataInputs[receivedDI->name()]->d_ptr->m_metadata = receivedDI->d_ptr->m_metadata;
}
}
- delete response;
Q_EMIT q_ptr->dataInputsReady();
break;
}
case CommandType_RequestDataOutputs: {
- QVariantList *response = reinterpret_cast<QVariantList *>(requestData);
-
for (int i = 0; i < response->size(); ++i) {
// Check and append to QML-side list if the (UIA) presentation has additional
// dataoutputs that are not explicitly defined in QML code.
if (!m_dataOutputs.contains(response->at(i).value<QString>()))
registerDataOutput(new Q3DSDataOutput(response->at(i).value<QString>(), nullptr));
}
- delete response;
Q_EMIT q_ptr->dataOutputsReady();
break;
}
+ case CommandType_RequestExportShaderCache: {
+ if (response->size() > 0)
+ m_shaderCacheExport = response->at(0).toByteArray();
+ if (!m_shaderCacheExport.isEmpty())
+ m_shaderCacheExport = qCompress(m_shaderCacheExport);
+ if (!m_shaderCacheWritePending.isEmpty()) {
+ writeShaderCache(m_shaderCacheWritePending);
+ m_shaderCacheWritePending.clear();
+ }
+ if (m_shaderCacheDumpPending) {
+ m_shaderCacheDumpPending = false;
+ dumpShaderCache();
+ }
+ break;
+ }
default:
Q_ASSERT(false);
break;
}
+ delete response;
+}
+
+// Writes current shader cache to the specified file in UTF-8 format
+void Q3DSPresentationPrivate::writeShaderCache(const QUrl &shaderCacheFile)
+{
+ if (m_shaderCacheExport.isEmpty())
+ return; // Warning is already printed by export function
+ const QString filePath = shaderCacheFile.toLocalFile();
+ QSaveFile file(filePath);
+ QFileInfo(filePath).dir().mkpath(QStringLiteral("."));
+ bool success = false;
+ if (file.open(QIODevice::WriteOnly) && file.write(m_shaderCacheExport) != -1) {
+ file.commit();
+ success = true;
+ } else {
+ qWarning() << __FUNCTION__ << "Warning: Failed to write shader cache:"
+ << shaderCacheFile << file.errorString();
+ }
+ Q_EMIT q_ptr->shaderCacheExported(success);
+}
+
+QByteArray Q3DSPresentationPrivate::loadShaderCache() const
+{
+ if (!m_shaderCacheFile.isEmpty()) {
+ QFile file(Q3DSUtils::urlToLocalFileOrQrc(m_shaderCacheFile));
+ if (file.open(QIODevice::ReadOnly))
+ return qUncompress(file.readAll());
+
+ qWarning() << __FUNCTION__ << "Warning: Failed to read shader cache:"
+ << m_shaderCacheFile << file.errorString();
+ }
+ return {};
+}
+
+void Q3DSPresentationPrivate::exportShaderCache(bool binaryShaders, bool dumpCache)
+{
+ if (m_viewerApp) {
+ m_shaderCacheExport = m_viewerApp->exportShaderCache(binaryShaders);
+ if (!m_shaderCacheExport.isEmpty())
+ m_shaderCacheExport = qCompress(m_shaderCacheExport);
+ if (dumpCache)
+ dumpShaderCache();
+ } else if (m_commandQueue) {
+ m_commandQueue->queueCommand(CommandType_RequestExportShaderCache, binaryShaders);
+ m_shaderCacheDumpPending = dumpCache;
+ }
+}
+
+void Q3DSPresentationPrivate::dumpShaderCache()
+{
+ if (!m_shaderCacheExport.isEmpty()) {
+ // Can't just dump the whole thing into a single qWarning() call, since at least on
+ // windows long strings are not printed out. qWarning() is used to make the dump go to
+ // stderr, which is less likely to get cluttered with other messages.
+ qWarning() << "-- Shader cache base64 dump start --";
+ const QString cacheDump = QString::fromLatin1(m_shaderCacheExport.toBase64());
+ for (int i = 0; i < cacheDump.size(); i += 100)
+ qWarning().noquote() << cacheDump.mid(i, 100);
+ qWarning() << "-- Shader cache base64 dump end --";
+ }
+ Q_EMIT q_ptr->shaderCacheExported(!m_shaderCacheExport.isEmpty());
}
// Doc note: The ownership of the registered scenes remains with the caller, who needs to
diff --git a/src/api/studio3d/q3dspresentation.h b/src/api/studio3d/q3dspresentation.h
index 91e76c5..af84073 100644
--- a/src/api/studio3d/q3dspresentation.h
+++ b/src/api/studio3d/q3dspresentation.h
@@ -58,6 +58,7 @@ class Q_STUDIO3D_EXPORT Q3DSPresentation : public QObject
Q_PROPERTY(QStringList createdElements READ createdElements NOTIFY elementsCreated)
Q_PROPERTY(QStringList createdMaterials READ createdMaterials NOTIFY materialsCreated)
Q_PROPERTY(QStringList createdMeshes READ createdMeshes NOTIFY meshesCreated)
+ Q_PROPERTY(QUrl shaderCacheFile READ shaderCacheFile WRITE setShaderCacheFile NOTIFY shaderCacheFileChanged )
public:
explicit Q3DSPresentation(QObject *parent = nullptr);
@@ -90,6 +91,10 @@ public:
Q_INVOKABLE void preloadSlide(const QString &elementPath);
Q_INVOKABLE void unloadSlide(const QString &elementPath);
+ Q_INVOKABLE void exportShaderCache(const QUrl &shaderCacheFile, bool binaryShaders);
+ Q_INVOKABLE void exportShaderCache(bool binaryShaders);
+ QUrl shaderCacheFile() const;
+
// Input event handlers
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
@@ -131,6 +136,7 @@ public Q_SLOTS:
void fireEvent(const QString &elementPath, const QString &eventName);
void setGlobalAnimationTime(qint64 milliseconds);
void setDataInputValue(const QString &name, const QVariant &value, bool force = false);
+ void setShaderCacheFile(const QUrl &fileName);
Q_SIGNALS:
void variantListChanged(const QStringList &variantList);
@@ -144,6 +150,8 @@ Q_SIGNALS:
void elementsCreated(const QStringList &elementPaths, const QString &error);
void materialsCreated(const QStringList &materialNames, const QString &error);
void meshesCreated(const QStringList &meshNames, const QString &error);
+ void shaderCacheFileChanged(const QUrl &fileName);
+ void shaderCacheExported(bool success);
private:
Q_DISABLE_COPY(Q3DSPresentation)
diff --git a/src/api/studio3d/q3dspresentation_p.h b/src/api/studio3d/q3dspresentation_p.h
index 5ade08e..45231b4 100644
--- a/src/api/studio3d/q3dspresentation_p.h
+++ b/src/api/studio3d/q3dspresentation_p.h
@@ -73,6 +73,7 @@ public:
void setDelayedLoading(bool enable);
void setDataInputsChanged(bool changed);
void setDataInputDirty(const QString &name, bool dirty);
+ void setShaderCacheFile(const QUrl &fileName);
void registerElement(Q3DSElement *element);
void unregisterElement(Q3DSElement *element);
@@ -100,6 +101,11 @@ public:
void requestResponseHandler(CommandType commandType, void *requestData);
+ void writeShaderCache(const QUrl &shaderCacheFile);
+ QByteArray loadShaderCache() const;
+ void exportShaderCache(bool binaryShaders, bool dumpCache);
+ void dumpShaderCache();
+
public Q_SLOTS:
void handleSlideEntered(const QString &elementPath, unsigned int index, const QString &name);
void handleDataOutputValueUpdate(const QString &name, const QVariant &newValue);
@@ -124,6 +130,10 @@ private:
QStringList m_createdMaterials;
QStringList m_createdMeshes;
bool m_dataInputsChanged;
+ QUrl m_shaderCacheFile;
+ QByteArray m_shaderCacheExport;
+ QUrl m_shaderCacheWritePending;
+ bool m_shaderCacheDumpPending = false;
friend class Q3DSStudio3D;
};
diff --git a/src/api/studio3d/q3dssurfaceviewer.cpp b/src/api/studio3d/q3dssurfaceviewer.cpp
index 1ffad89..e885a4b 100644
--- a/src/api/studio3d/q3dssurfaceviewer.cpp
+++ b/src/api/studio3d/q3dssurfaceviewer.cpp
@@ -662,6 +662,7 @@ bool Q3DSSurfaceViewerPrivate::initializeRuntime()
m_context->format(), int(m_fboId), localSource,
m_presentation->variantList(),
m_presentation->delayedLoading(), true,
+ m_presentation->d_ptr->loadShaderCache(),
m_presentation->d_ptr->streamProxy())) {
setError(m_viewerApp->error());
releaseRuntime();
diff --git a/src/api/studio3dqml/q3dsrenderer.cpp b/src/api/studio3dqml/q3dsrenderer.cpp
index 274c691..5a99aad 100644
--- a/src/api/studio3dqml/q3dsrenderer.cpp
+++ b/src/api/studio3dqml/q3dsrenderer.cpp
@@ -104,8 +104,11 @@ void Q3DSRenderer::synchronize(QQuickFramebufferObject *inView)
m_presentation->setVariantList(m_commands.m_variantList);
m_presentation->setSource(m_commands.m_source);
m_presentation->setDelayedLoading(m_commands.m_delayedLoading);
+ m_presentation->setShaderCacheFile(m_commands.m_shaderCacheFile);
m_commands.m_sourceChanged = false;
m_commands.m_variantListChanged = false;
+ m_commands.m_delayedLoadingChanged = false;
+ m_commands.m_shaderCacheFileChanged = false;
m_initialized = false;
m_initializationFailure = false;
m_error.clear();
@@ -243,7 +246,7 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo)
m_runtime, theWidth, theHeight, QOpenGLContext::currentContext()->format(),
int(inFbo->handle()), localSource, m_presentation->variantList(),
m_presentation->delayedLoading(), m_visitor, context,
- m_asyncInitSurface);
+ m_asyncInitSurface, m_presentation->d_ptr->loadShaderCache());
connect(m_runtimeInitializerThread, &Q3DSRuntimeInitializerThread::initDone,
this, &Q3DSRenderer::handleRuntimeInitializedAsync, Qt::QueuedConnection);
context->moveToThread(m_runtimeInitializerThread);
@@ -254,6 +257,7 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo)
int(inFbo->handle()), localSource,
m_presentation->variantList(),
m_presentation->delayedLoading(), true,
+ m_presentation->d_ptr->loadShaderCache(),
m_visitor)) {
m_error = m_runtime->error();
releaseRuntime();
@@ -472,6 +476,12 @@ void Q3DSRenderer::processCommands()
command.m_data = nullptr;
break;
}
+ case CommandType_PreloadSlide:
+ m_runtime->preloadSlide(cmd.m_elementPath);
+ break;
+ case CommandType_UnloadSlide:
+ m_runtime->unloadSlide(cmd.m_elementPath);
+ break;
case CommandType_RequestSlideInfo: {
int current = 0;
int previous = 0;
@@ -486,7 +496,6 @@ void Q3DSRenderer::processCommands()
requestData->append(QVariant(previousName));
Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData);
-
break;
}
case CommandType_RequestDataInputs: {
@@ -518,12 +527,15 @@ void Q3DSRenderer::processCommands()
Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData);
break;
}
- case CommandType_PreloadSlide:
- m_runtime->preloadSlide(cmd.m_elementPath);
- break;
- case CommandType_UnloadSlide:
- m_runtime->unloadSlide(cmd.m_elementPath);
+ case CommandType_RequestExportShaderCache: {
+ QByteArray shaderCache = m_runtime->exportShaderCache(cmd.m_boolValue);
+ QVariantList *requestData = new QVariantList();
+ requestData->append(QVariant(shaderCache));
+
+ Q_EMIT requestResponse({}, cmd.m_commandType, requestData);
break;
+ }
+
default:
qWarning() << __FUNCTION__ << "Unrecognized CommandType in command list!";
}
diff --git a/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp b/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp
index 677b61a..bc021aa 100644
--- a/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp
+++ b/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp
@@ -39,7 +39,7 @@ Q3DSRuntimeInitializerThread::Q3DSRuntimeInitializerThread(
Q3DSViewer::Q3DSViewerApp *runtime,
int width, int height, const QSurfaceFormat &format, int offscreenID, const QString &source,
const QStringList &variantList, bool delayedLoading, qt3ds::Qt3DSAssetVisitor *assetVisitor,
- QOpenGLContext *context, QSurface *surface)
+ QOpenGLContext *context, QSurface *surface, const QByteArray &shaderCache)
: m_runtime(runtime)
, m_width(width)
, m_height(height)
@@ -51,6 +51,7 @@ Q3DSRuntimeInitializerThread::Q3DSRuntimeInitializerThread(
, m_assetVisitor(assetVisitor)
, m_context(context)
, m_surface(surface)
+ , m_shaderCache(shaderCache)
{
}
@@ -60,7 +61,7 @@ void Q3DSRuntimeInitializerThread::run()
m_context->makeCurrent(m_surface);
m_success = m_runtime->InitializeApp(m_width, m_height, m_format, m_offscreenId,
m_source, m_variantList, m_delayedLoading, false,
- m_assetVisitor);
+ m_shaderCache, m_assetVisitor);
m_context->doneCurrent();
delete m_context;
diff --git a/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h b/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h
index 42dcf67..9aba691 100644
--- a/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h
+++ b/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h
@@ -64,7 +64,7 @@ public:
int offscreenID, const QString &source,
const QStringList &variantList, bool delayedLoading,
qt3ds::Qt3DSAssetVisitor *assetVisitor, QOpenGLContext *context,
- QSurface *surface);
+ QSurface *surface, const QByteArray &shaderCache);
void run() override;
@@ -86,6 +86,7 @@ private:
qt3ds::Qt3DSAssetVisitor *m_assetVisitor;
QOpenGLContext *m_context;
QSurface *m_surface;
+ QByteArray m_shaderCache;
bool m_success = false;
};
diff --git a/src/api/studio3dqml/q3dsstudio3d.cpp b/src/api/studio3dqml/q3dsstudio3d.cpp
index 19cf09b..4cb0850 100644
--- a/src/api/studio3dqml/q3dsstudio3d.cpp
+++ b/src/api/studio3dqml/q3dsstudio3d.cpp
@@ -366,7 +366,11 @@ void Q3DSStudio3D::reset()
m_pendingCommands.m_sourceChanged = true;
m_pendingCommands.m_source = m_presentation ? m_presentation->source() : QString();
m_pendingCommands.m_variantListChanged = true;
- m_pendingCommands.m_variantList = m_presentation ? m_presentation->variantList() : QStringList();
+ m_pendingCommands.m_variantList = m_presentation ? m_presentation->variantList()
+ : QStringList();
+ m_pendingCommands.m_shaderCacheFileChanged = true;
+ m_pendingCommands.m_shaderCacheFile = m_presentation ? m_presentation->shaderCacheFile()
+ : QString();
}
/*!
@@ -385,23 +389,16 @@ void Q3DSStudio3D::requestResponseHandler(const QString &elementPath, CommandTyp
qWarning() << __FUNCTION__ << "RequestSlideInfo response got for unregistered scene.";
break;
}
- case CommandType_RequestDataInputs: {
+ case CommandType_RequestDataInputs:
+ case CommandType_RequestDataOutputs:
+ case CommandType_RequestExportShaderCache:
+ {
Q3DSPresentation *handler = qobject_cast<Q3DSPresentation *>(m_presentation);
if (handler) {
handler->d_ptr->requestResponseHandler(commandType, requestData);
} else {
- qWarning() << __FUNCTION__
- << "RequestDataInputs response got for invalid presentation.";
- }
- break;
- }
- case CommandType_RequestDataOutputs: {
- Q3DSPresentation *handler = qobject_cast<Q3DSPresentation *>(m_presentation);
- if (handler) {
- handler->d_ptr->requestResponseHandler(commandType, requestData);
- } else {
- qWarning() << __FUNCTION__
- << "RequestDataOutputs response got for invalid presentation.";
+ qWarning() << __FUNCTION__ << "Command " << commandType
+ << "response got for invalid presentation.";
}
break;
}
diff --git a/src/engine/Qt3DSRenderRuntimeBinding.cpp b/src/engine/Qt3DSRenderRuntimeBinding.cpp
index ec89aa4..33f9f5c 100644
--- a/src/engine/Qt3DSRenderRuntimeBinding.cpp
+++ b/src/engine/Qt3DSRenderRuntimeBinding.cpp
@@ -814,8 +814,6 @@ struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager,
qt3ds::foundation::CFileTools::AppendDirectoryInPathToFile(theBinaryPath, "binary");
eastl::string theBinaryDir(theBinaryPath);
qt3ds::foundation::CFileTools::GetDirectory(theBinaryDir);
- if (m_Context->m_WriteOutShaderCache)
- qt3ds::foundation::CFileTools::CreateDir(theBinaryDir.c_str());
}
inScene.m_RuntimePresentation = &inPresentation;
m_Scenes.push_back(make_pair(&inPresentation, &inScene));
diff --git a/src/engine/Qt3DSRenderRuntimeBindingImpl.h b/src/engine/Qt3DSRenderRuntimeBindingImpl.h
index 877c6ea..1f0e565 100644
--- a/src/engine/Qt3DSRenderRuntimeBindingImpl.h
+++ b/src/engine/Qt3DSRenderRuntimeBindingImpl.h
@@ -96,7 +96,6 @@ namespace render {
QSize m_WindowDimensions;
eastl::string m_PrimitivePath;
bool m_RenderRotationsEnabled;
- bool m_WriteOutShaderCache;
volatile QT3DSI32 mRefCount;
Q3DStudio::IWindowSystem &m_WindowSystem;
Q3DStudio::ITimeProvider &m_TimeProvider;
@@ -110,7 +109,6 @@ namespace render {
, m_CoreContext(IQt3DSRenderContextCore::Create(*m_Foundation, *m_StringTable))
, m_PrimitivePath(inPrimitivePath)
, m_RenderRotationsEnabled(false)
- , m_WriteOutShaderCache(false)
, mRefCount(0)
, m_WindowSystem(inWindowSystem)
, m_TimeProvider(inTimeProvider)
diff --git a/src/engine/Qt3DSRenderRuntimeBindingImplRenderer.cpp b/src/engine/Qt3DSRenderRuntimeBindingImplRenderer.cpp
index 3aaa006..47ed8aa 100644
--- a/src/engine/Qt3DSRenderRuntimeBindingImplRenderer.cpp
+++ b/src/engine/Qt3DSRenderRuntimeBindingImplRenderer.cpp
@@ -91,7 +91,10 @@ struct SRenderer : public Q3DStudio::ITegraApplicationRenderEngine
m_BindingCore->m_WindowDimensions = theWindowDims;
m_BindingCore->m_Context->SetWindowDimensions(m_BindingCore->m_WindowDimensions);
}
- Q3DStudio::BOOL LoadShaderCache(const char * /*inFilePath*/) override { return true; }
+ QByteArray exportShaderCache(bool binaryShaders) override
+ {
+ return m_BindingCore->m_Context->GetShaderCache().exportShaderCache(binaryShaders);
+ }
void AbandonLoadingImages(Q3DStudio::IScene & /*inScene*/) override {}
@@ -168,11 +171,6 @@ struct SRenderer : public Q3DStudio::ITegraApplicationRenderEngine
m_BindingCore->m_RenderRotationsEnabled = inEnable;
}
- void SetWriteOutShaderCache(bool inWriteOutShaderCache) override
- {
- m_BindingCore->m_WriteOutShaderCache = inWriteOutShaderCache;
- }
-
Q3DStudio::ITegraRenderStateManager &GetTegraRenderStateManager() override { return m_RSM; }
qt3ds::render::NVRenderContext &GetRenderContext() override
{
diff --git a/src/engine/Qt3DSRuntimeView.cpp b/src/engine/Qt3DSRuntimeView.cpp
index f9a4768..3ab577c 100644
--- a/src/engine/Qt3DSRuntimeView.cpp
+++ b/src/engine/Qt3DSRuntimeView.cpp
@@ -171,7 +171,7 @@ public:
bool BeginLoad(const QString &sourcePath, const QStringList &variantList) override;
bool HasOfflineLoadingCompleted() override;
bool InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading,
- bool initInRenderThread) override;
+ bool initInRenderThread, const QByteArray &shaderCache) override;
void connectSignals() override;
void finishAsyncInit() override;
@@ -237,6 +237,7 @@ public:
void unloadSlide(const QString &slide) override;
void setDelayedLoading(bool enable) override;
void BootupPreGraphicsInitObjects();
+ QByteArray exportShaderCache(bool binaryShaders);
};
CRuntimeView::CRuntimeView(ITimeProvider &inTimeProvider, IWindowSystem &inWindowSystem,
@@ -298,7 +299,7 @@ bool CRuntimeView::HasOfflineLoadingCompleted()
}
bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading,
- bool initInRenderThread)
+ bool initInRenderThread, const QByteArray &shaderCache)
{
m_ApplicationCore->EndLoad();
// Next call will initialize the render portion of the scenes. This *must* have a loaded
@@ -306,7 +307,8 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed
m_RuntimeFactory = m_RuntimeFactoryCore->CreateRenderFactory(format, delayedLoading);
m_Application
= m_ApplicationCore->CreateApplication(*m_InputEngine, m_AudioPlayer,
- *m_RuntimeFactory, initInRenderThread);
+ *m_RuntimeFactory, shaderCache,
+ initInRenderThread);
if (!m_Application->createSuccessful())
return false;
diff --git a/src/engine/Qt3DSRuntimeView.h b/src/engine/Qt3DSRuntimeView.h
index 2d79601..1ddc48b 100644
--- a/src/engine/Qt3DSRuntimeView.h
+++ b/src/engine/Qt3DSRuntimeView.h
@@ -133,7 +133,7 @@ public:
virtual void SetApplicationViewport(const qt3ds::render::NVRenderRect &inViewport) = 0;
virtual void ensureRenderTarget() = 0;
virtual void CheckResize(bool inForce, IPresentation &inActivePresentation) = 0;
- virtual BOOL LoadShaderCache(const CHAR *inFilePath) = 0;
+ virtual QByteArray exportShaderCache(bool binaryShaders) = 0;
virtual void AbandonLoadingImages(IScene &inScene) = 0;
virtual BOOL IsPickValid(FLOAT &outX, FLOAT &outY,
const IPresentation &inPresentation) const = 0;
@@ -159,7 +159,6 @@ public:
virtual void SetMatteColor(qt3ds::foundation::Option<qt3ds::QT3DSVec4> inColor) = 0;
virtual void setMatteEnabled(bool enable) = 0;
virtual void EnableRenderRotation(bool inEnable) = 0;
- virtual void SetWriteOutShaderCache(bool inWriteOutShaderCache) = 0;
virtual void Release() = 0;
virtual void RenderText2D(FLOAT x, FLOAT y, qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor,
const char *text) = 0;
@@ -176,7 +175,7 @@ public: // loading
virtual bool BeginLoad(const QString &sourcePath, const QStringList &variantList) = 0;
virtual bool HasOfflineLoadingCompleted() = 0;
virtual bool InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading,
- bool initInRenderThread) = 0;
+ bool initInRenderThread, const QByteArray &shaderCache) = 0;
virtual void connectSignals() = 0;
virtual void finishAsyncInit() = 0;
diff --git a/src/render/Qt3DSRenderContext.cpp b/src/render/Qt3DSRenderContext.cpp
index aa2cb30..8a01e65 100644
--- a/src/render/Qt3DSRenderContext.cpp
+++ b/src/render/Qt3DSRenderContext.cpp
@@ -610,9 +610,10 @@ namespace render {
*this, shaderName, vertShader, fragShader, tessControlShaderSource,
tessEvaluationShaderSource, geometryShaderSource, false, type, true);
- if (result.mShader != NULL)
+ if (result.mShader != nullptr) {
m_ShaderToImpMap.insert(
make_pair(result.mShader->GetShaderProgramHandle(), result.mShader));
+ }
return result;
#else
@@ -621,6 +622,20 @@ namespace render {
#endif
}
+ NVRenderVertFragCompilationResult NVRenderContextImpl::CompileBinary(
+ const char *shaderName, QT3DSU32 format, const QByteArray &binary)
+ {
+ NVRenderVertFragCompilationResult result = NVRenderShaderProgram::createFromBinary(
+ *this, shaderName, format, binary);
+
+ if (result.mShader != nullptr) {
+ m_ShaderToImpMap.insert(
+ make_pair(result.mShader->GetShaderProgramHandle(), result.mShader));
+ }
+
+ return result;
+ }
+
NVRenderVertFragCompilationResult
NVRenderContextImpl::CompileComputeSource(const char *shaderName,
NVConstDataRef<QT3DSI8> computeShaderSource)
@@ -628,9 +643,10 @@ namespace render {
NVRenderVertFragCompilationResult result =
NVRenderShaderProgram::CreateCompute(*this, shaderName, computeShaderSource);
- if (result.mShader != NULL)
+ if (result.mShader != nullptr) {
m_ShaderToImpMap.insert(
make_pair(result.mShader->GetShaderProgramHandle(), result.mShader));
+ }
return result;
}
diff --git a/src/render/Qt3DSRenderContext.h b/src/render/Qt3DSRenderContext.h
index dfaf1bb..b454a3f 100644
--- a/src/render/Qt3DSRenderContext.h
+++ b/src/render/Qt3DSRenderContext.h
@@ -152,6 +152,7 @@ namespace render {
virtual bool IsAdvancedBlendHwSupportedKHR() const = 0;
virtual bool IsStandardDerivativesSupported() const = 0;
virtual bool IsTextureLodSupported() const = 0;
+ virtual bool isBinaryProgramSupported() const = 0;
virtual bool isSceneCameraView() const = 0;
virtual void SetDefaultRenderTarget(QT3DSU64 targetID) = 0;
@@ -268,6 +269,8 @@ namespace render {
NVDataRef<QT3DSI8> tessControlShaderSource = NVDataRef<QT3DSI8>(),
NVDataRef<QT3DSI8> tessEvaluationShaderSource = NVDataRef<QT3DSI8>(),
NVConstDataRef<QT3DSI8> geometryShaderSource = NVConstDataRef<QT3DSI8>()) = 0;
+ virtual NVRenderVertFragCompilationResult
+ CompileBinary(const char *shaderName, QT3DSU32 format, const QByteArray &binary) = 0;
virtual NVRenderVertFragCompilationResult
CompileComputeSource(const char *shaderName, NVConstDataRef<QT3DSI8> computeShaderSource) = 0;
@@ -779,6 +782,10 @@ namespace render {
{
return GetRenderBackendCap(NVRenderBackend::NVRenderBackendCaps::TextureLod);
}
+ bool isBinaryProgramSupported() const override
+ {
+ return GetRenderBackendCap(NVRenderBackend::NVRenderBackendCaps::BinaryProgram);
+ }
bool isSceneCameraView() const override
{
@@ -913,7 +920,8 @@ namespace render {
NVDataRef<QT3DSI8> tessControlShaderSource = NVDataRef<QT3DSI8>(),
NVDataRef<QT3DSI8> tessEvaluationShaderSource = NVDataRef<QT3DSI8>(),
NVConstDataRef<QT3DSI8> geometryShaderSource = NVConstDataRef<QT3DSI8>()) override;
-
+ NVRenderVertFragCompilationResult CompileBinary(const char *shaderName, QT3DSU32 format,
+ const QByteArray &binary) override;
virtual NVRenderVertFragCompilationResult
CompileComputeSource(const char *shaderName, NVConstDataRef<QT3DSI8> computeShaderSource) override;
diff --git a/src/render/Qt3DSRenderShaderProgram.cpp b/src/render/Qt3DSRenderShaderProgram.cpp
index 09f1094..cc8c1fd 100644
--- a/src/render/Qt3DSRenderShaderProgram.cpp
+++ b/src/render/Qt3DSRenderShaderProgram.cpp
@@ -553,16 +553,17 @@ namespace render {
cbSize, cbCount, pBuffer, alloc);
}
- bool NVRenderShaderProgram::Link()
+ bool NVRenderShaderProgram::link(QT3DSU32 binaryFormat, const QByteArray *binary)
{
- bool success = m_Backend->LinkProgram(m_ProgramHandle, m_ErrorMessage);
+ bool success = m_Backend->linkProgram(m_ProgramHandle, m_ErrorMessage,
+ binaryFormat, binary);
if (success) {
char nameBuf[512];
QT3DSI32 location, elementCount, binding;
NVRenderShaderDataTypes::Enum type;
- QT3DSI32 constantCount = m_Backend->GetConstantCount(m_ProgramHandle);
+ QT3DSU32 constantCount = QT3DSU32(m_Backend->GetConstantCount(m_ProgramHandle));
QT3DS_FOREACH(idx, constantCount)
{
@@ -578,20 +579,23 @@ namespace render {
}
if (location != -1) {
CRegisteredString theName(m_Context.GetStringTable().RegisterStr(nameBuf));
- m_Constants.insert(eastl::make_pair(
- theName,
- ShaderConstantFactory(m_Backend, theName, m_Context.GetFoundation(),
- location, elementCount, type, binding)));
+ m_Constants.insert(
+ eastl::make_pair(
+ theName,
+ ShaderConstantFactory(m_Backend, theName,
+ m_Context.GetFoundation(),
+ location, elementCount, type, binding)));
}
}
// next query constant buffers info
QT3DSI32 length, bufferSize, paramCount;
- QT3DSI32 constantBufferCount = m_Backend->GetConstantBufferCount(m_ProgramHandle);
+ QT3DSU32 constantBufferCount = QT3DSU32(
+ m_Backend->GetConstantBufferCount(m_ProgramHandle));
QT3DS_FOREACH(idx, constantBufferCount)
{
location = m_Backend->GetConstantBufferInfoByID(
- m_ProgramHandle, idx, 512, &paramCount, &bufferSize, &length, nameBuf);
+ m_ProgramHandle, idx, 512, &paramCount, &bufferSize, &length, nameBuf);
if (location != -1) {
CRegisteredString theName(m_Context.GetStringTable().RegisterStr(nameBuf));
@@ -603,20 +607,23 @@ namespace render {
cb->addRef();
}
- m_ShaderBuffers.insert(eastl::make_pair(
- theName,
- ShaderBufferFactory<NVRenderShaderConstantBuffer, NVRenderConstantBuffer>(
- m_Context, theName, m_Context.GetFoundation(), location, -1, bufferSize,
- paramCount, cb)));
+ m_ShaderBuffers.insert(
+ eastl::make_pair(
+ theName,
+ ShaderBufferFactory<NVRenderShaderConstantBuffer,
+ NVRenderConstantBuffer>(
+ m_Context, theName, m_Context.GetFoundation(), location, -1,
+ bufferSize, paramCount, cb)));
}
}
// next query storage buffers
- QT3DSI32 storageBufferCount = m_Backend->GetStorageBufferCount(m_ProgramHandle);
+ QT3DSU32 storageBufferCount = QT3DSU32(
+ m_Backend->GetStorageBufferCount(m_ProgramHandle));
QT3DS_FOREACH(idx, storageBufferCount)
{
location = m_Backend->GetStorageBufferInfoByID(
- m_ProgramHandle, idx, 512, &paramCount, &bufferSize, &length, nameBuf);
+ m_ProgramHandle, idx, 512, &paramCount, &bufferSize, &length, nameBuf);
if (location != -1) {
CRegisteredString theName(m_Context.GetStringTable().RegisterStr(nameBuf));
@@ -627,11 +634,13 @@ namespace render {
sb->addRef();
}
- m_ShaderBuffers.insert(eastl::make_pair(
- theName,
- ShaderBufferFactory<NVRenderShaderStorageBuffer, NVRenderStorageBuffer>(
- m_Context, theName, m_Context.GetFoundation(), location, -1, bufferSize,
- paramCount, sb)));
+ m_ShaderBuffers.insert(
+ eastl::make_pair(
+ theName,
+ ShaderBufferFactory<NVRenderShaderStorageBuffer,
+ NVRenderStorageBuffer>(
+ m_Context, theName, m_Context.GetFoundation(), location, -1,
+ bufferSize, paramCount, sb)));
}
}
@@ -640,7 +649,7 @@ namespace render {
QT3DS_FOREACH(idx, atomicBufferCount)
{
location = m_Backend->GetAtomicCounterBufferInfoByID(
- m_ProgramHandle, idx, 512, &paramCount, &bufferSize, &length, nameBuf);
+ m_ProgramHandle, idx, 512, &paramCount, &bufferSize, &length, nameBuf);
if (location != -1) {
CRegisteredString theName(m_Context.GetStringTable().RegisterStr(nameBuf));
@@ -655,16 +664,18 @@ namespace render {
// We get the actual buffer name by searching for this uniform name
// See NVRenderTestAtomicCounterBuffer.cpp how the setup works
NVRenderAtomicCounterBuffer *acb =
- m_Context.GetAtomicCounterBufferByParam(theName);
+ m_Context.GetAtomicCounterBufferByParam(theName);
if (acb) {
acb->addRef();
- m_ShaderBuffers.insert(eastl::make_pair(
- acb->GetBufferName(),
- ShaderBufferFactory<NVRenderShaderAtomicCounterBuffer,
- NVRenderAtomicCounterBuffer>(
- m_Context, acb->GetBufferName(), m_Context.GetFoundation(),
- location, -1, bufferSize, paramCount, acb)));
+ m_ShaderBuffers.insert(
+ eastl::make_pair(
+ acb->GetBufferName(),
+ ShaderBufferFactory<NVRenderShaderAtomicCounterBuffer,
+ NVRenderAtomicCounterBuffer>(
+ m_Context, acb->GetBufferName(),
+ m_Context.GetFoundation(),
+ location, -1, bufferSize, paramCount, acb)));
}
}
}
@@ -1096,7 +1107,7 @@ namespace render {
pProgram->Attach(geShader.getValue());
// link program
- bProgramIsValid = pProgram->Link();
+ bProgramIsValid = pProgram->link();
}
}
@@ -1189,6 +1200,35 @@ namespace render {
return result;
}
+ NVRenderVertFragCompilationResult NVRenderShaderProgram::createFromBinary(
+ NVRenderContextImpl &context, const char *programName, QT3DSU32 format,
+ const QByteArray &binary)
+ {
+ NVRenderVertFragCompilationResult result;
+ NVRenderShaderProgram *pProgram = nullptr;
+ bool bProgramIsValid = false;
+
+ result.mShaderName = programName;
+
+ pProgram = QT3DS_NEW(context.GetFoundation().getAllocator(), NVRenderShaderProgram)(
+ context, context.GetFoundation(), programName, false);
+
+ bProgramIsValid = pProgram->link(format, &binary);
+
+ if (!bProgramIsValid && pProgram) {
+ NVFoundationBase &foundation(context.GetFoundation());
+ qCCritical(INTERNAL_ERROR, "Failed to link binary program!!");
+ WriteErrorMessage(foundation, "Program link output:", pProgram->GetErrorMessage());
+
+ // delete program
+ QT3DS_FREE(context.GetFoundation().getAllocator(), pProgram);
+ pProgram = nullptr;
+ }
+
+ result.mShader = pProgram;
+ return result;
+ }
+
NVRenderVertFragCompilationResult
NVRenderShaderProgram::CreateCompute(NVRenderContextImpl &context, const char *programName,
NVConstDataRef<QT3DSI8> computeShaderSource)
@@ -1219,7 +1259,7 @@ namespace render {
pProgram->Attach(&computeShader);
// link program
- bProgramIsValid = pProgram->Link();
+ bProgramIsValid = pProgram->link();
// set program type
pProgram->SetProgramType(ProgramType::Compute);
diff --git a/src/render/Qt3DSRenderShaderProgram.h b/src/render/Qt3DSRenderShaderProgram.h
index 054db64..ed8c677 100644
--- a/src/render/Qt3DSRenderShaderProgram.h
+++ b/src/render/Qt3DSRenderShaderProgram.h
@@ -192,7 +192,7 @@ namespace render {
*
* @return true if succesfuly linked.
*/
- bool Link();
+ bool link(QT3DSU32 binaryFormat = 0, const QByteArray *binary = nullptr);
/**
* @brief set a shader type
@@ -356,6 +356,11 @@ namespace render {
return m_ProgramHandle;
}
+ void getProgramBinary(QT3DSU32 &outFormat, QByteArray &outBinary) const
+ {
+ m_Backend->getProgramBinary(m_ProgramHandle, outFormat, outBinary);
+ }
+
/**
* @brief get the context object
*
@@ -389,6 +394,10 @@ namespace render {
NVRenderShaderProgramBinaryType::Enum type = NVRenderShaderProgramBinaryType::Unknown,
bool binaryProgram = false);
+ static NVRenderVertFragCompilationResult createFromBinary(
+ NVRenderContextImpl &context, const char *programName, QT3DSU32 format,
+ const QByteArray &binary);
+
/**
* @brief Create a compute shader program
*
diff --git a/src/render/backends/Qt3DSRenderBackend.h b/src/render/backends/Qt3DSRenderBackend.h
index 12de5d8..4e9e177 100644
--- a/src/render/backends/Qt3DSRenderBackend.h
+++ b/src/render/backends/Qt3DSRenderBackend.h
@@ -128,7 +128,8 @@ namespace render {
AdvancedBlendKHR, ///< Driver supports advanced blend modes
VertexArrayObject,
StandardDerivatives,
- TextureLod
+ TextureLod,
+ BinaryProgram
};
} NVRenderBackendCaps;
@@ -1547,8 +1548,9 @@ namespace render {
*
* @return True if program is succesful linked.
*/
- virtual bool LinkProgram(NVRenderBackendShaderProgramObject po,
- eastl::string &errorMessage) = 0;
+ virtual bool linkProgram(NVRenderBackendShaderProgramObject po,
+ eastl::string &errorMessage,
+ QT3DSU32 binaryFormat, const QByteArray *binary) = 0;
/**
* @brief Make a program current
@@ -2194,6 +2196,9 @@ namespace render {
virtual QSurfaceFormat format() const = 0;
+ virtual void getProgramBinary(NVRenderBackendShaderProgramObject po, QT3DSU32 &outFormat,
+ QByteArray &outBinary) = 0;
+
protected:
/// struct for what the backend supports
typedef struct NVRenderBackendSupport
@@ -2231,6 +2236,7 @@ namespace render {
bool bVertexArrayObjectSupported : 1;
bool bStandardDerivativesSupported : 1;
bool bTextureLodSupported : 1;
+ bool bBinaryProgramsSupported : 1;
} bits;
QT3DSU32 u32Values;
diff --git a/src/render/backends/gl/Q3DSRenderBackendGLES2.cpp b/src/render/backends/gl/Q3DSRenderBackendGLES2.cpp
index 02046ac..1db51ef 100644
--- a/src/render/backends/gl/Q3DSRenderBackendGLES2.cpp
+++ b/src/render/backends/gl/Q3DSRenderBackendGLES2.cpp
@@ -143,6 +143,9 @@ NVRenderBackendGLES2Impl::NVRenderBackendGLES2Impl(NVFoundationBase &fnd,
// constant buffers support is always not true
m_backendSupport.caps.bits.bConstantBufferSupported = false;
+ // Binary programs are never supported
+ m_backendSupport.caps.bits.bBinaryProgramsSupported = false;
+
// query hardware
GL_CALL_EXTRA_FUNCTION(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &m_MaxAttribCount));
diff --git a/src/render/backends/gl/Qt3DSRenderBackendGL3.cpp b/src/render/backends/gl/Qt3DSRenderBackendGL3.cpp
index c4d8b64..e5caf5e 100644
--- a/src/render/backends/gl/Qt3DSRenderBackendGL3.cpp
+++ b/src/render/backends/gl/Qt3DSRenderBackendGL3.cpp
@@ -134,6 +134,7 @@ namespace render {
m_backendSupport.caps.bits.bStandardDerivativesSupported = true;
m_backendSupport.caps.bits.bVertexArrayObjectSupported = true;
m_backendSupport.caps.bits.bTextureLodSupported = true;
+ m_backendSupport.caps.bits.bBinaryProgramsSupported = true;
if (!isESCompatible()) {
// render to float textures is always supported on none ES systems which support >=GL3
diff --git a/src/render/backends/gl/Qt3DSRenderBackendGL4.cpp b/src/render/backends/gl/Qt3DSRenderBackendGL4.cpp
index 083fc35..4ff532d 100644
--- a/src/render/backends/gl/Qt3DSRenderBackendGL4.cpp
+++ b/src/render/backends/gl/Qt3DSRenderBackendGL4.cpp
@@ -130,6 +130,7 @@ namespace render {
// always true for GL4.1 and GLES 3.1 devices
m_backendSupport.caps.bits.bMsTextureSupported = true;
m_backendSupport.caps.bits.bProgramPipelineSupported = true;
+ m_backendSupport.caps.bits.bBinaryProgramsSupported = true;
if (!isESCompatible()) {
// TODO: investigate GL 4.0 support
diff --git a/src/render/backends/gl/Qt3DSRenderBackendGLBase.cpp b/src/render/backends/gl/Qt3DSRenderBackendGLBase.cpp
index 5dfe9d1..3505564 100644
--- a/src/render/backends/gl/Qt3DSRenderBackendGLBase.cpp
+++ b/src/render/backends/gl/Qt3DSRenderBackendGLBase.cpp
@@ -241,6 +241,9 @@ bool NVRenderBackendGLBase::GetRenderBackendCap(
case NVRenderBackendCaps::TextureLod:
bSupported = m_backendSupport.caps.bits.bTextureLodSupported;
break;
+ case NVRenderBackendCaps::BinaryProgram:
+ bSupported = m_backendSupport.caps.bits.bBinaryProgramsSupported;
+ break;
default:
QT3DS_ASSERT(false);
bSupported = false;
@@ -1584,14 +1587,21 @@ void NVRenderBackendGLBase::ReleaseShaderProgram(NVRenderBackendShaderProgramObj
NVDelete(m_Foundation.getAllocator(), pProgram);
}
-bool NVRenderBackendGLBase::LinkProgram(NVRenderBackendShaderProgramObject po,
- eastl::string &errorMessage)
+bool NVRenderBackendGLBase::linkProgram(NVRenderBackendShaderProgramObject po,
+ eastl::string &errorMessage,
+ QT3DSU32 binaryFormat, const QByteArray *binary)
{
NVRenderBackendShaderProgramGL *pProgram = (NVRenderBackendShaderProgramGL *)po;
GLuint programID = static_cast<GLuint>(pProgram->m_ProgramID);
- GL_CALL_FUNCTION(glLinkProgram(programID));
+ if (binary) {
+ GL_CALL_EXTRA_FUNCTION(glProgramBinary(programID, GLenum(binaryFormat), binary->constData(),
+ binary->size()));
+ } else {
+ GL_CALL_FUNCTION(glLinkProgram(programID));
+ }
+ // TODO: Move to a another function to be also used after binary link?
GLint linkStatus, logLen;
GL_CALL_FUNCTION(glGetProgramiv(programID, GL_LINK_STATUS, &linkStatus));
GL_CALL_FUNCTION(glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &logLen));
@@ -1617,7 +1627,7 @@ bool NVRenderBackendGLBase::LinkProgram(NVRenderBackendShaderProgramObject po,
GLint maxLength;
GL_CALL_FUNCTION(glGetProgramiv(programID, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength));
QT3DSI8 *nameBuf =
- (QT3DSI8 *)QT3DS_ALLOC(m_Foundation.getAllocator(), maxLength, "LinkProgram");
+ (QT3DSI8 *)QT3DS_ALLOC(m_Foundation.getAllocator(), maxLength, "linkProgram");
// fill in data
QT3DSU32 count = 0;
@@ -2147,6 +2157,22 @@ void NVRenderBackendGLBase::CoverStrokePathInstanced(NVRenderBackendPathObject,
qCCritical(INVALID_OPERATION) << QObject::tr("Unsupported method: ") << __FUNCTION__;
}
+void NVRenderBackendGLBase::getProgramBinary(NVRenderBackend::NVRenderBackendShaderProgramObject po,
+ QT3DSU32 &outFormat, QByteArray &outBinary)
+{
+ NVRenderBackendShaderProgramGL *pProgram
+ = reinterpret_cast<NVRenderBackendShaderProgramGL *>(po);
+ GLuint programID = static_cast<GLuint>(pProgram->m_ProgramID);
+ GLint binLen = 0;
+
+ GL_CALL_FUNCTION(glGetProgramiv(programID, GL_PROGRAM_BINARY_LENGTH, &binLen));
+
+ outBinary.resize(binLen);
+
+ GL_CALL_EXTRA_FUNCTION(glGetProgramBinary(programID, binLen, nullptr, &outFormat,
+ outBinary.data()));
+}
+
///< private calls
const char *NVRenderBackendGLBase::getVersionString()
{
diff --git a/src/render/backends/gl/Qt3DSRenderBackendGLBase.h b/src/render/backends/gl/Qt3DSRenderBackendGLBase.h
index 2691118..5b18161 100644
--- a/src/render/backends/gl/Qt3DSRenderBackendGLBase.h
+++ b/src/render/backends/gl/Qt3DSRenderBackendGLBase.h
@@ -343,8 +343,9 @@ namespace render {
NVRenderBackendComputeShaderObject cso) override;
NVRenderBackendShaderProgramObject CreateShaderProgram(bool isSeparable) override;
void ReleaseShaderProgram(NVRenderBackendShaderProgramObject po) override;
- bool LinkProgram(NVRenderBackendShaderProgramObject po,
- eastl::string &errorMessage) override;
+ bool linkProgram(NVRenderBackendShaderProgramObject po,
+ eastl::string &errorMessage,
+ QT3DSU32 binaryFormat, const QByteArray *binary) override;
void SetActiveProgram(NVRenderBackendShaderProgramObject po) override;
void DispatchCompute(NVRenderBackendShaderProgramObject po, QT3DSU32 numGroupsX,
QT3DSU32 numGroupsY, QT3DSU32 numGroupsZ) override;
@@ -489,6 +490,9 @@ namespace render {
return m_format;
}
+ void getProgramBinary(NVRenderBackendShaderProgramObject po, QT3DSU32 &outFormat,
+ QByteArray &outBinary) override;
+
protected:
virtual NVFoundationBase &GetFoundation() { return m_Foundation; }
virtual bool compileSource(GLuint shaderID, NVConstDataRef<QT3DSI8> source,
diff --git a/src/render/backends/software/Qt3DSRenderBackendNULL.cpp b/src/render/backends/software/Qt3DSRenderBackendNULL.cpp
index 2908816..1f93a1f 100644
--- a/src/render/backends/software/Qt3DSRenderBackendNULL.cpp
+++ b/src/render/backends/software/Qt3DSRenderBackendNULL.cpp
@@ -409,7 +409,8 @@ struct SNullBackend : public NVRenderBackend
}
void ReleaseProgramPipeline(NVRenderBackendProgramPipeline) override {}
- bool LinkProgram(NVRenderBackendShaderProgramObject, eastl::string &) override { return false; }
+ bool linkProgram(NVRenderBackendShaderProgramObject, eastl::string &,
+ QT3DSU32, const QByteArray *) override { return false; }
void SetActiveProgram(NVRenderBackendShaderProgramObject) override {}
void SetActiveProgramPipeline(NVRenderBackendProgramPipeline) override {}
void SetProgramStages(NVRenderBackendProgramPipeline, NVRenderShaderTypeFlags,
@@ -579,6 +580,9 @@ struct SNullBackend : public NVRenderBackend
{
return QSurfaceFormat();
}
+ void getProgramBinary(NVRenderBackendShaderProgramObject, QT3DSU32 &, QByteArray &) override
+ {
+ }
};
}
diff --git a/src/runtime/Qt3DSApplication.cpp b/src/runtime/Qt3DSApplication.cpp
index 63632f0..198bf3e 100644
--- a/src/runtime/Qt3DSApplication.cpp
+++ b/src/runtime/Qt3DSApplication.cpp
@@ -433,7 +433,6 @@ struct SApplicationSettings
{
Option<bool> m_LayerCacheEnabled;
Option<bool> m_LayerGpuProfilingEnabled;
- Option<bool> m_ShaderCachePersistenceEnabled;
SApplicationSettings() {}
@@ -452,8 +451,6 @@ struct SApplicationSettings
Choose(inCommandLine.m_LayerCacheEnabled, inUIAFileSettings.m_LayerCacheEnabled))
, m_LayerGpuProfilingEnabled(Choose(inCommandLine.m_LayerGpuProfilingEnabled,
inUIAFileSettings.m_LayerGpuProfilingEnabled))
- , m_ShaderCachePersistenceEnabled(Choose(inCommandLine.m_ShaderCachePersistenceEnabled,
- inUIAFileSettings.m_ShaderCachePersistenceEnabled))
{
}
@@ -511,7 +508,6 @@ struct SApplicationSettings
{
ParseBoolEnableDisableItem(inReader, LayerCacheName(), m_LayerCacheEnabled);
ParseBoolEnableDisableItem(inReader, LayerGpuProfilerName(), m_LayerGpuProfilingEnabled);
- ParseBoolEnableDisableItem(inReader, ShaderCacheName(), m_ShaderCachePersistenceEnabled);
}
void Parse(IDOMReader &inReader) { ParseItems(inReader); }
@@ -543,7 +539,6 @@ struct SApplicationSettings
{
outStream.Write(SOptionSerializer(m_LayerCacheEnabled));
outStream.Write(SOptionSerializer(m_LayerGpuProfilingEnabled));
- outStream.Write(SOptionSerializer(m_ShaderCachePersistenceEnabled));
}
void Load(IInStream &inStream)
@@ -553,8 +548,6 @@ struct SApplicationSettings
m_LayerCacheEnabled = s;
inStream.Read(s);
m_LayerGpuProfilingEnabled = s;
- inStream.Read(s);
- m_ShaderCachePersistenceEnabled = s;
}
};
@@ -1745,6 +1738,7 @@ struct SApp : public IApplication
IApplication &CreateApplication(Q3DStudio::CInputEngine &inInputEngine,
Q3DStudio::IAudioPlayer *inAudioPlayer,
Q3DStudio::IRuntimeFactory &inFactory,
+ const QByteArray &shaderCache,
bool initInRenderThread) override
{
{
@@ -1811,6 +1805,10 @@ struct SApp : public IApplication
*finalSettings.m_LayerCacheEnabled);
}
+
+ if (!shaderCache.isEmpty())
+ inFactory.GetQt3DSRenderContext().GetShaderCache().importShaderCache(shaderCache);
+
m_CoreFactory->GetPerfTimer().OutputTimerData();
m_AudioPlayer.SetPlayer(inAudioPlayer);
diff --git a/src/runtime/Qt3DSApplication.h b/src/runtime/Qt3DSApplication.h
index 13499f6..deaafe7 100644
--- a/src/runtime/Qt3DSApplication.h
+++ b/src/runtime/Qt3DSApplication.h
@@ -250,6 +250,7 @@ public:
virtual IApplication &CreateApplication(Q3DStudio::CInputEngine &inInputEngine,
Q3DStudio::IAudioPlayer *inAudioPlayer,
Q3DStudio::IRuntimeFactory &inFactory,
+ const QByteArray &shaderCache,
bool initInRenderThread) = 0;
// maintains reference to runtime factory core. AppDir is where the executable is located;
diff --git a/src/runtimerender/Qt3DSRenderShaderCache.cpp b/src/runtimerender/Qt3DSRenderShaderCache.cpp
index 77d4ee6..c642360 100644
--- a/src/runtimerender/Qt3DSRenderShaderCache.cpp
+++ b/src/runtimerender/Qt3DSRenderShaderCache.cpp
@@ -44,135 +44,19 @@
#include "foundation/Qt3DSPerfTimer.h"
#include "EASTL/sort.h"
-#include <QRegularExpression>
-#include <QString>
+#include <QtCore/qstring.h>
+#include <QtCore/qdatastream.h>
using namespace qt3ds::render;
namespace {
using qt3ds::render::NVRenderContextScopedProperty;
-const char *TessellationEnabledStr = "TessellationStageEnabled";
-const char *GeometryEnabledStr = "GeometryStageEnabled";
-inline void AppendFlagValue(Qt3DSString &inStr, const char *flag)
-{
- if (inStr.length())
- inStr.append(QLatin1Char(','));
- inStr.append(flag);
-}
-inline void CacheFlagsToStr(const SShaderCacheProgramFlags &inFlags, Qt3DSString &inString)
-{
- inString.clear();
- if (inFlags.IsTessellationEnabled())
- AppendFlagValue(inString, TessellationEnabledStr);
- if (inFlags.IsGeometryShaderEnabled())
- AppendFlagValue(inString, GeometryEnabledStr);
-}
struct ShaderType
{
enum Enum { Vertex, TessControl, TessEval, Fragment, Geometry, Compute };
};
-inline ShaderType::Enum StringToShaderType(Qt3DSString &inShaderType)
-{
- ShaderType::Enum retval = ShaderType::Vertex;
-
- if (inShaderType.size() == 0)
- return retval;
-
- if (!inShaderType.compare("VertexCode"))
- retval = ShaderType::Vertex;
- else if (!inShaderType.compare("FragmentCode"))
- retval = ShaderType::Fragment;
- else if (!inShaderType.compare("TessControlCode"))
- retval = ShaderType::TessControl;
- else if (!inShaderType.compare("TessEvalCode"))
- retval = ShaderType::TessEval;
- else if (!inShaderType.compare("GeometryCode"))
- retval = ShaderType::Geometry;
- else
- QT3DS_ASSERT(false);
-
- return retval;
-}
-
-inline SShaderCacheProgramFlags CacheFlagsToStr(const Qt3DSString &inString)
-{
- SShaderCacheProgramFlags retval;
- if (inString.indexOf(TessellationEnabledStr) != Qt3DSString::npos)
- retval.SetTessellationEnabled(true);
- if (inString.indexOf(GeometryEnabledStr) != Qt3DSString::npos)
- retval.SetGeometryShaderEnabled(true);
- return retval;
-}
-
-typedef eastl::pair<const char *, NVRenderContextValues::Enum> TStringToContextValuePair;
-
-/*GLES2 = 1 << 0,
-GL2 = 1 << 1,
-GLES3 = 1 << 2,
-GL3 = 1 << 3,
-GL4 = 1 << 4,
-NullContext = 1 << 5,*/
-TStringToContextValuePair g_StringToContextTypeValue[] = {
- TStringToContextValuePair("GLES2", NVRenderContextValues::GLES2),
- TStringToContextValuePair("GL2", NVRenderContextValues::GL2),
- TStringToContextValuePair("GLES3", NVRenderContextValues::GLES3),
- TStringToContextValuePair("GLES3PLUS", NVRenderContextValues::GLES3PLUS),
- TStringToContextValuePair("GL3", NVRenderContextValues::GL3),
- TStringToContextValuePair("GL4", NVRenderContextValues::GL4),
- TStringToContextValuePair("NullContext", NVRenderContextValues::NullContext),
-};
-
-size_t g_NumStringToContextValueEntries =
- sizeof(g_StringToContextTypeValue) / sizeof(*g_StringToContextTypeValue);
-
-inline void ContextTypeToString(qt3ds::render::NVRenderContextType inType,
- Qt3DSString &outContextType)
-{
- outContextType.clear();
- for (size_t idx = 0, end = g_NumStringToContextValueEntries; idx < end; ++idx) {
- if (inType & g_StringToContextTypeValue[idx].second) {
- if (outContextType.size())
- outContextType.append('|');
- outContextType.append(g_StringToContextTypeValue[idx].first);
- }
- }
-}
-
-inline qt3ds::render::NVRenderContextType StringToContextType(const Qt3DSString &inContextType)
-{
- qt3ds::render::NVRenderContextType retval;
- char tempBuffer[128];
- memZero(tempBuffer, 128);
- const QString::size_type lastTempBufIdx = 127;
- QString::size_type pos = 0, lastpos = 0;
- if (inContextType.size() == 0)
- return retval;
-
- do {
- pos = int(inContextType.indexOf(QLatin1Char('|'), lastpos));
- if (pos == Qt3DSString::npos)
- pos = int(inContextType.size());
- {
-
- QString::size_type sectionLen = NVMin(pos - lastpos, lastTempBufIdx);
- qt3ds::intrinsics::memCopy(tempBuffer, inContextType.toUtf8().constData() + lastpos,
- sectionLen);
- tempBuffer[lastTempBufIdx] = 0;
- for (size_t idx = 0, end = g_NumStringToContextValueEntries; idx < end; ++idx) {
- if (strcmp(g_StringToContextTypeValue[idx].first, tempBuffer) == 0)
- retval = retval | g_StringToContextTypeValue[idx].second;
- }
- }
- // iterate past the bar
- ++pos;
- lastpos = pos;
- } while (pos < inContextType.size() && pos != Qt3DSString::npos);
-
- return retval;
-}
-
struct SShaderCacheKey
{
CRegisteredString m_Key;
@@ -204,7 +88,7 @@ struct SShaderCacheKey
{
m_HashCode = m_Key.hash();
m_HashCode = m_HashCode
- ^ HashShaderFeatureSet(toDataRef(m_Features.data(), (QT3DSU32)m_Features.size()));
+ ^ HashShaderFeatureSet(toDataRef(m_Features.data(), QT3DSU32(m_Features.size())));
}
bool operator==(const SShaderCacheKey &inOther) const
{
@@ -236,14 +120,26 @@ struct ShaderCache : public IShaderCache
Qt3DSString m_GeometryCode;
Qt3DSString m_FragmentCode;
Qt3DSString m_InsertStr;
- Qt3DSString m_FlagString;
Qt3DSString m_ContextTypeString;
SShaderCacheKey m_TempKey;
- NVScopedRefCounted<IDOMWriter> m_ShaderCache;
IInputStreamFactory &m_InputStreamFactory;
- bool m_ShaderCompilationEnabled;
- volatile QT3DSI32 mRefCount;
+ bool m_ShaderCompilationEnabled = true;
+ bool m_shadersInitializedFromCache = false;
+ volatile QT3DSI32 mRefCount = 0;
+
+ struct ShaderSource
+ {
+ QVector<SShaderPreprocessorFeature> features;
+ CRegisteredString key;
+ SShaderCacheProgramFlags flags;
+ QByteArray vertexCode;
+ QByteArray tessCtrlCode;
+ QByteArray tessEvalCode;
+ QByteArray geometryCode;
+ QByteArray fragmentCode;
+ };
+ QVector<ShaderSource> m_shaderSourceCache;
ShaderCache(NVRenderContext &ctx, IInputStreamFactory &inInputStreamFactory,
IPerfTimer &inPerfTimer)
@@ -251,14 +147,12 @@ struct ShaderCache : public IShaderCache
, m_PerfTimer(inPerfTimer)
, m_Shaders(ctx.GetAllocator(), "ShaderCache::m_Shaders")
, m_InputStreamFactory(inInputStreamFactory)
- , m_ShaderCompilationEnabled(true)
- , mRefCount(0)
{
}
QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator())
- NVRenderShaderProgram *GetProgram(CRegisteredString inKey,
- NVConstDataRef<SShaderPreprocessorFeature> inFeatures) override
+ NVRenderShaderProgram *GetProgram(
+ CRegisteredString inKey, NVConstDataRef<SShaderPreprocessorFeature> inFeatures) override
{
m_TempKey.m_Key = inKey;
m_TempKey.m_Features.assign(inFeatures.begin(), inFeatures.end());
@@ -266,7 +160,7 @@ struct ShaderCache : public IShaderCache
TShaderMap::iterator theIter = m_Shaders.find(m_TempKey);
if (theIter != m_Shaders.end())
return theIter->second;
- return NULL;
+ return nullptr;
}
void AddBackwardCompatibilityDefines(ShaderType::Enum shaderType)
@@ -332,7 +226,8 @@ struct ShaderCache : public IShaderCache
void AddShaderPreprocessor(Qt3DSString &str, CRegisteredString inKey,
ShaderType::Enum shaderType,
- NVConstDataRef<SShaderPreprocessorFeature> inFeatures)
+ NVConstDataRef<SShaderPreprocessorFeature> inFeatures,
+ bool forCache)
{
// Don't use shading language version returned by the driver as it might
// differ from the context version. Instead use the context type to specify
@@ -343,7 +238,7 @@ struct ShaderCache : public IShaderCache
QString versionStr;
QTextStream stream(&versionStr);
stream << "#version ";
- const QT3DSU32 type = (QT3DSU32)m_RenderContext.GetRenderContextType();
+ const QT3DSU32 type = QT3DSU32(m_RenderContext.GetRenderContextType());
switch (type) {
case NVRenderContextValues::GLES2:
stream << "1" << minor << "0\n";
@@ -436,7 +331,9 @@ struct ShaderCache : public IShaderCache
}
}
- if (inKey.IsValid()) {
+ // Adding shader name is useful debugging feature, but it is not particularly useful when
+ // creating shaders for cache, and it can be long
+ if (inKey.IsValid() && !forCache) {
m_InsertStr += "//Shader name -";
m_InsertStr += inKey.c_str();
m_InsertStr += "\n";
@@ -452,7 +349,35 @@ struct ShaderCache : public IShaderCache
str.insert(0, m_InsertStr);
}
+
// Compile this program overwriting any existing ones.
+ void addShaderPreprocessors(CRegisteredString inKey,
+ const SShaderCacheProgramFlags &inFlags,
+ NVConstDataRef<SShaderPreprocessorFeature> inFeatures,
+ bool separableProgram, bool forCache)
+ {
+ // Add defines and such so we can write unified shaders that work across platforms.
+ // vertex and fragment shaders are optional for separable shaders
+ if (!separableProgram || !m_VertexCode.isEmpty())
+ AddShaderPreprocessor(m_VertexCode, inKey, ShaderType::Vertex, inFeatures, forCache);
+ if (!separableProgram || !m_FragmentCode.isEmpty()) {
+ AddShaderPreprocessor(m_FragmentCode, inKey, ShaderType::Fragment, inFeatures,
+ forCache);
+ }
+ // optional shaders
+ if (inFlags.IsTessellationEnabled()) {
+ QT3DS_ASSERT(m_TessCtrlCode.size() && m_TessEvalCode.size());
+ AddShaderPreprocessor(m_TessCtrlCode, inKey, ShaderType::TessControl, inFeatures,
+ forCache);
+ AddShaderPreprocessor(m_TessEvalCode, inKey, ShaderType::TessEval, inFeatures,
+ forCache);
+ }
+ if (inFlags.IsGeometryShaderEnabled()) {
+ AddShaderPreprocessor(m_GeometryCode, inKey, ShaderType::Geometry, inFeatures,
+ forCache);
+ }
+ }
+
NVRenderShaderProgram *
ForceCompileProgram(CRegisteredString inKey, const char8_t *inVert, const char8_t *inFrag,
const char8_t *inTessCtrl, const char8_t *inTessEval, const char8_t *inGeom,
@@ -461,7 +386,7 @@ struct ShaderCache : public IShaderCache
bool separableProgram, bool fromDisk = false) override
{
if (m_ShaderCompilationEnabled == false)
- return NULL;
+ return nullptr;
SShaderCacheKey tempKey(inKey);
tempKey.m_Features.assign(inFeatures.begin(), inFeatures.end());
tempKey.GenerateHashCode();
@@ -492,20 +417,9 @@ struct ShaderCache : public IShaderCache
m_TessEvalCode.assign(inTessEval);
m_GeometryCode.assign(inGeom);
m_FragmentCode.assign(inFrag);
- // Add defines and such so we can write unified shaders that work across platforms.
- // vertex and fragment shaders are optional for separable shaders
- if (!separableProgram || !m_VertexCode.isEmpty())
- AddShaderPreprocessor(m_VertexCode, inKey, ShaderType::Vertex, inFeatures);
- if (!separableProgram || !m_FragmentCode.isEmpty())
- AddShaderPreprocessor(m_FragmentCode, inKey, ShaderType::Fragment, inFeatures);
- // optional shaders
- if (inFlags.IsTessellationEnabled()) {
- QT3DS_ASSERT(m_TessCtrlCode.size() && m_TessEvalCode.size());
- AddShaderPreprocessor(m_TessCtrlCode, inKey, ShaderType::TessControl, inFeatures);
- AddShaderPreprocessor(m_TessEvalCode, inKey, ShaderType::TessEval, inFeatures);
- }
- if (inFlags.IsGeometryShaderEnabled())
- AddShaderPreprocessor(m_GeometryCode, inKey, ShaderType::Geometry, inFeatures);
+
+ if (!fromDisk)
+ addShaderPreprocessors(inKey, inFlags, inFeatures, separableProgram, false);
theInserter.first->second =
m_RenderContext
@@ -515,49 +429,26 @@ struct ShaderCache : public IShaderCache
m_TessEvalCode.c_str(), QT3DSU32(m_TessEvalCode.size()),
m_GeometryCode.c_str(), QT3DSU32(m_GeometryCode.size()),
separableProgram).mShader;
- if (theInserter.first->second) {
- if (m_ShaderCache) {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "Program");
- m_ShaderCache->Att("key", inKey.c_str());
- CacheFlagsToStr(inFlags, m_FlagString);
- if (m_FlagString.size())
- m_ShaderCache->Att("glflags", m_FlagString.c_str());
- // write out the GL version.
- {
- qt3ds::render::NVRenderContextType theContextType =
- m_RenderContext.GetRenderContextType();
- ContextTypeToString(theContextType, m_ContextTypeString);
- m_ShaderCache->Att("gl-context-type", m_ContextTypeString.c_str());
- }
- if (inFeatures.size()) {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "Features");
- for (QT3DSU32 idx = 0, end = inFeatures.size(); idx < end; ++idx) {
- m_ShaderCache->Att(inFeatures[idx].m_Name, inFeatures[idx].m_Enabled);
- }
- }
- {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "VertexCode");
- m_ShaderCache->Value(inVert);
- }
- {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "FragmentCode");
- m_ShaderCache->Value(inFrag);
- }
- if (m_TessCtrlCode.size()) {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "TessControlCode");
- m_ShaderCache->Value(inTessCtrl);
- }
- if (m_TessEvalCode.size()) {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "TessEvalCode");
- m_ShaderCache->Value(inTessEval);
- }
- if (m_GeometryCode.size()) {
- IDOMWriter::Scope __writeScope(*m_ShaderCache, "GeometryCode");
- m_ShaderCache->Value(inGeom);
- }
- }
+ // This is unnecessary memory waste in final deployed product, so we don't store this
+ // information when shaders were initialized from a cache.
+ // Unfortunately it is not practical to just regenerate shader source from scratch, when we
+ // want to export it, as the triggers and original sources are spread all over the place.
+ if (!m_shadersInitializedFromCache && theInserter.first->second) {
+ // Store sources for possible cache generation later
+ ShaderSource ss;
+ for (QT3DSU32 i = 0, end = inFeatures.size(); i < end; ++i)
+ ss.features.append(inFeatures[i]);
+ ss.key = inKey;
+ ss.flags = inFlags;
+ ss.vertexCode = inVert;
+ ss.fragmentCode = inFrag;
+ ss.tessCtrlCode = inTessCtrl;
+ ss.tessEvalCode = inTessEval;
+ ss.geometryCode = inGeom;
+ m_shaderSourceCache.append(ss);
}
+
return theInserter.first->second;
}
@@ -574,162 +465,183 @@ struct ShaderCache : public IShaderCache
NVRenderShaderProgram *retval =
ForceCompileProgram(inKey, inVert, inFrag, inTessCtrl, inTessEval, inGeom, inFlags,
inFeatures, separableProgram);
- if (m_CacheFilePath.c_str() && m_ShaderCache && m_ShaderCompilationEnabled) {
- CFileSeekableIOStream theStream(m_CacheFilePath.c_str(), FileWriteFlags());
- if (theStream.IsOpen()) {
- NVScopedRefCounted<IStringTable> theStringTable(
- IStringTable::CreateStringTable(m_RenderContext.GetAllocator()));
- CDOMSerializer::WriteXMLHeader(theStream);
- CDOMSerializer::Write(m_RenderContext.GetAllocator(),
- *m_ShaderCache->GetTopElement(), theStream, *theStringTable);
- }
- }
return retval;
}
- void BootupDOMWriter()
+ // Magic number to identify cache file type
+ const quint32 shaderCacheFileId = 0x26a9b358;
+
+ QByteArray exportShaderCache(bool binaryShaders) override
{
- NVScopedRefCounted<IStringTable> theStringTable(
- IStringTable::CreateStringTable(m_RenderContext.GetAllocator()));
- m_ShaderCache = IDOMWriter::CreateDOMWriter(m_RenderContext.GetAllocator(),
- "Qt3DSShaderCache", theStringTable)
- .first;
- m_ShaderCache->Att("cache_version", IShaderCache::GetShaderVersion());
+ if (m_shadersInitializedFromCache) {
+ qWarning() << __FUNCTION__ << "Warning: Shader cache export is not supported when"
+ " shaders were originally imported from a cache file.";
+ return {};
+ }
+
+ // The assumption is that cache was generated on the same environment it will be read.
+ // Attempting to load a cache generated on another environment will likely lead to crash.
+
+ QByteArray retval;
+ QDataStream data(&retval, QIODevice::WriteOnly);
+ bool saveBinary = binaryShaders && m_RenderContext.isBinaryProgramSupported();
+ data << shaderCacheFileId;
+ data << saveBinary;
+ data << IShaderCache::shaderCacheVersion();
+ data << m_shaderSourceCache.size();
+
+ for (const auto &ss : qAsConst(m_shaderSourceCache))
+ {
+ data << QByteArray(ss.key.c_str());
+ data << ss.features.size();
+ for (int i = 0, end = ss.features.size(); i < end; ++i) {
+ data << QByteArray(ss.features[i].m_Name.c_str());
+ data << ss.features[i].m_Enabled;
+ }
+
+ if (saveBinary) {
+ TShaderFeatureSet features(ss.features.constData(), QT3DSU32(ss.features.size()));
+ NVRenderShaderProgram *program = GetProgram(ss.key, features);
+ QT3DSU32 format;
+ QByteArray binaryData;
+ program->getProgramBinary(format, binaryData);
+ data << format;
+ data << binaryData;
+ } else {
+ m_VertexCode.assign(ss.vertexCode.constData());
+ m_FragmentCode.assign(ss.fragmentCode.constData());
+ m_TessCtrlCode.assign(ss.tessCtrlCode.constData());
+ m_TessEvalCode.assign(ss.tessEvalCode.constData());
+ m_GeometryCode.assign(ss.geometryCode.constData());
+ addShaderPreprocessors(
+ ss.key, ss.flags,
+ qt3ds::foundation::toConstDataRef(
+ ss.features.constData(), static_cast<QT3DSU32>(ss.features.size())),
+ false, true);
+ auto writeShaderElement = [&data](const QByteArray &shaderSource) {
+ QByteArray stripped = shaderSource;
+ int start = stripped.indexOf(QLatin1String("/*"));
+ while (start != -1) {
+ int end = stripped.indexOf(QLatin1String("*/"));
+ if (end == -1)
+ break; // Mismatched comment
+ stripped.replace(start, end - start + 2, QByteArray());
+ start = stripped.indexOf(QLatin1String("/*"));
+ }
+ data << stripped;
+ };
+
+ writeShaderElement(m_VertexCode.toUtf8());
+ writeShaderElement(m_FragmentCode.toUtf8());
+ writeShaderElement(m_TessCtrlCode.toUtf8());
+ writeShaderElement(m_TessEvalCode.toUtf8());
+ writeShaderElement(m_GeometryCode.toUtf8());
+ }
+ }
+ return retval;
}
- void SetShaderCachePersistenceEnabled(const char8_t *inDirectory) override
+ void importShaderCache(const QByteArray &shaderCache) override
{
- if (inDirectory == NULL) {
- m_ShaderCache = NULL;
- return;
+ #define BAILOUT(details) { \
+ qWarning() << "importShaderCache failed to import shader cache:" << details; \
+ return; \
}
- BootupDOMWriter();
- m_CacheFilePath = QDir(inDirectory).filePath(GetShaderCacheFileName());
-
- NVScopedRefCounted<IRefCountedInputStream> theInStream =
- m_InputStreamFactory.GetStreamForFile(m_CacheFilePath.c_str());
- if (theInStream) {
- SStackPerfTimer __perfTimer(m_PerfTimer, "ShaderCache - Load");
- NVScopedRefCounted<IStringTable> theStringTable(
- IStringTable::CreateStringTable(m_RenderContext.GetAllocator()));
- NVScopedRefCounted<IDOMFactory> theFactory(
- IDOMFactory::CreateDOMFactory(m_RenderContext.GetAllocator(), theStringTable));
- eastl::vector<SShaderPreprocessorFeature> theFeatures;
-
- SDOMElement *theElem = CDOMSerializer::Read(*theFactory, *theInStream).second;
- if (theElem) {
- NVScopedRefCounted<IDOMReader> theReader = IDOMReader::CreateDOMReader(
- m_RenderContext.GetAllocator(), *theElem, theStringTable, theFactory);
- QT3DSU32 theAttValue = 0;
- theReader->Att("cache_version", theAttValue);
- if (theAttValue == IShaderCache::GetShaderVersion()) {
- Qt3DSString loadVertexData;
- Qt3DSString loadFragmentData;
- Qt3DSString loadTessControlData;
- Qt3DSString loadTessEvalData;
- Qt3DSString loadGeometryData;
- Qt3DSString shaderTypeString;
- IStringTable &theStringTable(m_RenderContext.GetStringTable());
- for (bool success = theReader->MoveToFirstChild(); success;
- success = theReader->MoveToNextSibling()) {
- const char8_t *theKeyStr = NULL;
- theReader->UnregisteredAtt("key", theKeyStr);
-
- CRegisteredString theKey = theStringTable.RegisterStr(theKeyStr);
- if (theKey.IsValid()) {
- m_FlagString.clear();
- const char8_t *theFlagStr = "";
- SShaderCacheProgramFlags theFlags;
- if (theReader->UnregisteredAtt("glflags", theFlagStr)) {
- m_FlagString.assign(theFlagStr);
- theFlags = CacheFlagsToStr(m_FlagString);
- }
-
- m_ContextTypeString.clear();
- if (theReader->UnregisteredAtt("gl-context-type", theFlagStr))
- m_ContextTypeString.assign(theFlagStr);
-
- theFeatures.clear();
- {
- IDOMReader::Scope __featureScope(*theReader);
- if (theReader->MoveToFirstChild("Features")) {
- for (SDOMAttribute *theAttribute =
- theReader->GetFirstAttribute();
- theAttribute;
- theAttribute = theAttribute->m_NextAttribute) {
- bool featureValue = false;
- StringConversion<bool>().StrTo(theAttribute->m_Value,
- featureValue);
- theFeatures.push_back(SShaderPreprocessorFeature(
- theStringTable.RegisterStr(
- theAttribute->m_Name.c_str()),
- featureValue));
- }
- }
- }
-
- qt3ds::render::NVRenderContextType theContextType =
- StringToContextType(m_ContextTypeString);
- if (((QT3DSU32)theContextType != 0)
- && (theContextType & m_RenderContext.GetRenderContextType())
- == theContextType) {
- IDOMReader::Scope __readerScope(*theReader);
- loadVertexData.clear();
- loadFragmentData.clear();
- loadTessControlData.clear();
- loadTessEvalData.clear();
- loadGeometryData.clear();
-
- // Vertex *MUST* be the first
- // Todo deal with pure compute shader programs
- if (theReader->MoveToFirstChild("VertexCode")) {
- const char8_t *theValue = NULL;
- theReader->Value(theValue);
- loadVertexData.assign(theValue);
- while (theReader->MoveToNextSibling()) {
- theReader->Value(theValue);
-
- shaderTypeString.assign(
- theReader->GetElementName().c_str());
- ShaderType::Enum shaderType =
- StringToShaderType(shaderTypeString);
-
- if (shaderType == ShaderType::Fragment)
- loadFragmentData.assign(theValue);
- else if (shaderType == ShaderType::TessControl)
- loadTessControlData.assign(theValue);
- else if (shaderType == ShaderType::TessEval)
- loadTessEvalData.assign(theValue);
- else if (shaderType == ShaderType::Geometry)
- loadGeometryData.assign(theValue);
- }
- }
-
- if (loadVertexData.size()
- && (loadFragmentData.size() || loadGeometryData.size())) {
-
- NVRenderShaderProgram *theShader = ForceCompileProgram(
- theKey, loadVertexData.c_str(), loadFragmentData.c_str(),
- loadTessControlData.c_str(), loadTessEvalData.c_str(),
- loadGeometryData.c_str(), theFlags,
- qt3ds::foundation::toDataRef(theFeatures.data(),
- (QT3DSU32)theFeatures.size()),
- false, true /*fromDisk*/);
- // If something doesn't save or load correctly, get the runtime
- // to re-generate.
- if (!theShader)
- m_Shaders.erase(theKey);
- }
- }
- }
- }
+
+ if (shaderCache.isEmpty())
+ BAILOUT("Shader cache Empty")
+
+ SStackPerfTimer __perfTimer(m_PerfTimer, "ShaderCache - Import");
+
+ QDataStream data(shaderCache);
+ quint32 type;
+ quint32 version;
+ bool isBinary;
+ data >> type;
+ if (type != shaderCacheFileId)
+ BAILOUT("Not a shader cache")
+ data >> isBinary;
+ if (isBinary && !m_RenderContext.isBinaryProgramSupported())
+ BAILOUT("Binary shaders are not supported")
+ data >> version;
+ if (version != IShaderCache::shaderCacheVersion())
+ BAILOUT("Version mismatch")
+
+ #undef BAILOUT
+
+ IStringTable &stringTable(m_RenderContext.GetStringTable());
+
+ int progCount;
+ data >> progCount;
+ m_shadersInitializedFromCache = progCount > 0;
+ for (int i = 0; i < progCount; ++i) {
+ QByteArray key;
+ int featCount;
+ CRegisteredString theKey;
+ data >> key;
+ data >> featCount;
+ theKey = stringTable.RegisterStr(key);
+ eastl::vector<SShaderPreprocessorFeature> features;
+ for (int j = 0; j < featCount; ++j) {
+ QByteArray featName;
+ bool featVal;
+ data >> featName;
+ data >> featVal;
+ CRegisteredString regName = stringTable.RegisterStr(featName);
+ features.push_back(SShaderPreprocessorFeature(regName, featVal));
+ }
+ NVRenderShaderProgram *theShader = nullptr;
+ if (isBinary) {
+ QT3DSU32 format;
+ QByteArray binary;
+ data >> format;
+ data >> binary;
+
+ SShaderCacheKey tempKey(theKey);
+ tempKey.m_Features.assign(features.begin(), features.end());
+ tempKey.GenerateHashCode();
+
+ qCInfo(TRACE_INFO) << "Loading binary program from shader cache: '<" << key << ">'";
+
+ eastl::pair<TShaderMap::iterator, bool> theInserter = m_Shaders.insert(tempKey);
+ theInserter.first->second
+ = m_RenderContext.CompileBinary(theKey, format, binary).mShader;
+ theShader = theInserter.first->second;
+ } else {
+ QByteArray loadVertexData;
+ QByteArray loadFragmentData;
+ QByteArray loadTessControlData;
+ QByteArray loadTessEvalData;
+ QByteArray loadGeometryData;
+
+ data >> loadVertexData;
+ data >> loadFragmentData;
+ data >> loadTessControlData;
+ data >> loadTessEvalData;
+ data >> loadGeometryData;
+
+ if (!loadVertexData.isEmpty() && (!loadFragmentData.isEmpty()
+ || !loadGeometryData.isEmpty())) {
+ theShader = ForceCompileProgram(
+ theKey, loadVertexData.constData(),
+ loadFragmentData.constData(),
+ loadTessControlData.constData(),
+ loadTessEvalData.constData(),
+ loadGeometryData.constData(),
+ SShaderCacheProgramFlags(),
+ qt3ds::foundation::toDataRef(
+ features.data(), static_cast<QT3DSU32>(features.size())),
+ false, true);
}
}
+ // If something doesn't save or load correctly, get the runtime to re-generate.
+ if (!theShader) {
+ qWarning() << __FUNCTION__ << "Failed to load a cached a shader:" << key;
+ m_Shaders.erase(theKey);
+ }
}
}
- bool IsShaderCachePersistenceEnabled() const override { return m_ShaderCache != NULL; }
-
void SetShaderCompilationEnabled(bool inEnableShaderCompilation) override
{
m_ShaderCompilationEnabled = inEnableShaderCompilation;
diff --git a/src/runtimerender/Qt3DSRenderShaderCache.h b/src/runtimerender/Qt3DSRenderShaderCache.h
index 47e76f3..ca333c6 100644
--- a/src/runtimerender/Qt3DSRenderShaderCache.h
+++ b/src/runtimerender/Qt3DSRenderShaderCache.h
@@ -98,18 +98,12 @@ namespace render {
protected:
virtual ~IShaderCache() {}
public:
- // If directory is nonnull, then we attempt to load any shaders from shadercache.xml in
- // inDirectory
- // and save any new ones out to the same file. The shaders are marked by the gl version
- // used when saving.
- // If we can't open shadercache.xml from inDirectory for writing (at least), then we still
- // consider the
- // shadercache to be disabled.
- // This call immediately blocks and attempts to load all applicable shaders from the
- // shadercache.xml file in
- // the given directory.
- virtual void SetShaderCachePersistenceEnabled(const char8_t *inDirectory) = 0;
- virtual bool IsShaderCachePersistenceEnabled() const = 0;
+ // Generates a shader cache file from the current contents of the shader cache
+ // If binaryShaders is true, the precompiled shaders are exported. Otherwise shader
+ // source code is exported
+ virtual QByteArray exportShaderCache(bool binaryShaders) = 0;
+ // This call immediately blocks and attempts to load all shaders from the shaderCache
+ virtual void importShaderCache(const QByteArray &shaderCache) = 0;
// It is up to the caller to ensure that inFeatures contains unique keys.
// It is also up the the caller to ensure the keys are ordered in some way.
virtual NVRenderShaderProgram *
@@ -147,8 +141,7 @@ namespace render {
virtual void SetShaderCompilationEnabled(bool inEnableShaderCompilation) = 0;
// Upping the shader version invalidates all previous cache files.
- static QT3DSU32 GetShaderVersion() { return 4; }
- static const char8_t *GetShaderCacheFileName() { return "shadercache.xml"; }
+ static quint32 shaderCacheVersion() { return 1; }
static IShaderCache &CreateShaderCache(NVRenderContext &inContext,
IInputStreamFactory &inInputStreamFactory,
@@ -157,4 +150,4 @@ namespace render {
}
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp
index 10f62c3..466ac5c 100644
--- a/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp
+++ b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp
@@ -100,7 +100,7 @@ namespace render {
, m_Context(ctx.GetRenderContext())
, m_BufferManager(ctx.GetBufferManager())
, m_OffscreenRenderManager(ctx.GetOffscreenRenderManager())
- , m_StringTable(IStringTable::CreateStringTable(ctx.GetAllocator()))
+ , m_StringTable(ctx.GetStringTable())
, m_LayerShaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LayerShaders")
, m_Shaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_Shaders")
, m_ConstantBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_ConstantBuffers")
diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp
index eb560aa..64de27d 100644
--- a/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp
+++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp
@@ -624,10 +624,10 @@ namespace render {
*GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment));
IShaderProgramGenerator::OutputParaboloidDepthVertex(vertexShader);
IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader);
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -699,9 +699,10 @@ namespace render {
tessEvalShader.Append("}");
IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader);
+
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
}
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -773,9 +774,10 @@ namespace render {
tessEvalShader.Append("}");
IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader);
+
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
}
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -847,9 +849,10 @@ namespace render {
tessEvalShader.Append("}");
IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader);
+
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
}
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -909,13 +912,10 @@ namespace render {
IShaderProgramGenerator::OutputCubeFaceDepthVertex(vertexShader);
// IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader );
IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader);
- } else if (theCache.IsShaderCachePersistenceEnabled()) {
- // we load from shader cache set default shader stages
- GetProgramGenerator().BeginProgram();
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -988,10 +988,10 @@ namespace render {
tessEvalShader.Append("\tworld_pos /= world_pos.w;");
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1070,10 +1070,10 @@ namespace render {
tessEvalShader.Append("\tworld_pos /= world_pos.w;");
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1158,10 +1158,10 @@ namespace render {
tessEvalShader.Append("\tworld_pos /= world_pos.w;");
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1223,10 +1223,10 @@ namespace render {
fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;");
fragmentShader.Append("\tfragOutput = vec4(depth);");
fragmentShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1294,10 +1294,10 @@ namespace render {
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;");
tessEvalShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1371,10 +1371,10 @@ namespace render {
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;");
tessEvalShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1457,10 +1457,10 @@ namespace render {
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;");
tessEvalShader.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1515,13 +1515,10 @@ namespace render {
fragmentShader.Append("void main() {");
fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);");
fragmentShader.Append("}");
- } else if (theCache.IsShaderCachePersistenceEnabled()) {
- // we load from shader cache set default shader stages
- GetProgramGenerator().BeginProgram();
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1667,18 +1664,13 @@ namespace render {
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;");
tessEvalShader.Append("}");
- } else if (theCache.IsShaderCachePersistenceEnabled()) {
- // we load from shader cache set default shader stages
- GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags(
- ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl
- | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment));
- }
- SShaderCacheProgramFlags theFlags;
- theFlags.SetTessellationEnabled(true);
+ SShaderCacheProgramFlags theFlags;
+ theFlags.SetTessellationEnabled(true);
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- name.c_str(), theFlags, TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ name.c_str(), theFlags, TShaderFeatureSet());
+ }
if (depthShaderProgram) {
theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1747,18 +1739,13 @@ namespace render {
tessEvalShader.Append("\tvec4 pos = tessShader( );\n");
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n");
tessEvalShader.Append("}");
- } else if (theCache.IsShaderCachePersistenceEnabled()) {
- // we load from shader cache set default shader stages
- GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags(
- ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl
- | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment));
- }
- SShaderCacheProgramFlags theFlags;
- theFlags.SetTessellationEnabled(true);
+ SShaderCacheProgramFlags theFlags;
+ theFlags.SetTessellationEnabled(true);
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- "depth tess phong prepass shader", theFlags, TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ "depth tess phong prepass shader", theFlags, TShaderFeatureSet());
+ }
if (depthShaderProgram) {
m_DepthTessPhongPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1841,18 +1828,13 @@ namespace render {
tessEvalShader.Append("\tvec4 pos = tessShader( );\n");
tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n");
tessEvalShader.Append("}");
- } else if (theCache.IsShaderCachePersistenceEnabled()) {
- // we load from shader cache set default shader stages
- GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags(
- ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl
- | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment));
- }
- SShaderCacheProgramFlags theFlags;
- theFlags.SetTessellationEnabled(true);
+ SShaderCacheProgramFlags theFlags;
+ theFlags.SetTessellationEnabled(true);
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- "depth tess npatch prepass shader", theFlags, TShaderFeatureSet());
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ "depth tess npatch prepass shader", theFlags, TShaderFeatureSet());
+ }
if (depthShaderProgram) {
m_DepthTessNPatchPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(
@@ -1974,10 +1956,10 @@ namespace render {
"\tgl_FragColor = vec4(aoFactor, aoFactor, aoFactor, 1.0);");
theFragmentGenerator.Append("}");
- }
- aoPassShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- "fullscreen AO pass shader", SShaderCacheProgramFlags(), inFeatureSet);
+ aoPassShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ "fullscreen AO pass shader", SShaderCacheProgramFlags(), inFeatureSet);
+ }
if (aoPassShaderProgram) {
m_DefaultAoPassShader = NVScopedRefCounted<SDefaultAoPassShader>(
@@ -2021,10 +2003,10 @@ namespace render {
"\tgl_FragColor = vec4( depSample, depSample, depSample, 1.0 );");
theFragmentGenerator.Append("\treturn;");
theFragmentGenerator.Append("}");
- }
- depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- "depth display shader", SShaderCacheProgramFlags(), inFeatureSet);
+ depthShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ "depth display shader", SShaderCacheProgramFlags(), inFeatureSet);
+ }
if (depthShaderProgram) {
m_FakeDepthShader = NVScopedRefCounted<SDefaultAoPassShader>(
@@ -2066,10 +2048,10 @@ namespace render {
theFragmentGenerator.Append(
"\tgl_FragColor = vec4(smpDepth, smpDepth, smpDepth, 1.0);");
theFragmentGenerator.Append("}");
- }
- cubeShaderProgram = GetProgramGenerator().CompileGeneratedShader(
- "cube depth display shader", SShaderCacheProgramFlags(), inFeatureSet);
+ cubeShaderProgram = GetProgramGenerator().CompileGeneratedShader(
+ "cube depth display shader", SShaderCacheProgramFlags(), inFeatureSet);
+ }
if (cubeShaderProgram) {
m_FakeCubemapDepthShader = NVScopedRefCounted<SDefaultAoPassShader>(
diff --git a/src/viewer/Qt3DSViewerApp.cpp b/src/viewer/Qt3DSViewerApp.cpp
index 56cb9b9..0e73734 100644
--- a/src/viewer/Qt3DSViewerApp.cpp
+++ b/src/viewer/Qt3DSViewerApp.cpp
@@ -391,6 +391,7 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor
int offscreenID, const QString &source,
const QStringList &variantList,
bool delayedLoading, bool initInRenderThread,
+ const QByteArray &shaderCache,
qt3ds::Qt3DSAssetVisitor *assetVisitor)
{
bool hasValidPresentationFile = !source.isEmpty();
@@ -428,7 +429,8 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor
}
bool success = m_Impl.m_view->InitializeGraphics(format, delayedLoading,
- initInRenderThread);
+ initInRenderThread,
+ shaderCache);
if (!success) {
m_Impl.m_error = QObject::tr("Viewer launch failure! Failed to load: '%1'").arg(source);
m_Impl.m_error.append("\n");
@@ -880,6 +882,14 @@ void Q3DSViewerApp::setMatteEnabled(bool enable)
m_Impl.m_view->GetTegraRenderEngine()->setMatteEnabled(enable);
}
+QByteArray Q3DSViewerApp::exportShaderCache(bool binaryShaders)
+{
+ if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine())
+ return m_Impl.m_view->GetTegraRenderEngine()->exportShaderCache(binaryShaders);
+
+ return {};
+}
+
void Q3DSViewerApp::setShowOnScreenStats(bool inShow)
{
if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine())
diff --git a/src/viewer/Qt3DSViewerApp.h b/src/viewer/Qt3DSViewerApp.h
index bcb6b81..e12057b 100644
--- a/src/viewer/Qt3DSViewerApp.h
+++ b/src/viewer/Qt3DSViewerApp.h
@@ -229,6 +229,7 @@ public:
int offscreenID, const QString &source,
const QStringList &variantList,
bool delayedLoading, bool initInRenderThread,
+ const QByteArray &shaderCache,
qt3ds::Qt3DSAssetVisitor *assetVisitor = nullptr);
void connectSignals();
@@ -496,6 +497,8 @@ public:
void setDelayedLoading(bool enable);
void setMatteEnabled(bool enabled);
+ QByteArray exportShaderCache(bool binaryShaders);
+
private:
/*
* @brief parse command line arguments this fills in our
diff --git a/tools/viewer/main.cpp b/tools/viewer/main.cpp
index 910e457..70da8fa 100644
--- a/tools/viewer/main.cpp
+++ b/tools/viewer/main.cpp
@@ -174,7 +174,7 @@ int main(int argc, char *argv[])
"Output filename base for the image\n"
"sequence.\n"
"The default value is derived from the presentation filename."),
- QCoreApplication::translate("main", "file"), QStringLiteral("")});
+ QCoreApplication::translate("main", "file"), QString()});
parser.addOption({"connect",
QCoreApplication::translate("main",
"If this parameter is specified, the viewer\n"
@@ -192,7 +192,7 @@ int main(int argc, char *argv[])
"Specifies the initial\n"
"window geometry using the X11-syntax.\n"
"For example: 1000x800+50+50"),
- QCoreApplication::translate("main", "geometry"), QStringLiteral("")});
+ QCoreApplication::translate("main", "geometry"), QString()});
parser.addOption({"mattecolor",
QCoreApplication::translate("main",
"Specifies custom matte color\n"
@@ -220,6 +220,10 @@ int main(int argc, char *argv[])
"The default value is 0.4"),
QCoreApplication::translate("main", "separation"),
QString::number(0.4)});
+ parser.addOption({"convert-shader-cache",
+ QCoreApplication::translate("main",
+ "Convert base64 dump to shader cache file."),
+ QCoreApplication::translate("main", "fileName"), QString()});
QCommandLineOption variantListOption({QStringLiteral("v"),
QStringLiteral("variants")},
QObject::tr("Gives list of variant groups and variants\n"
@@ -349,6 +353,24 @@ int main(int argc, char *argv[])
else
appWindow->setProperty("scaleMode", Q3DSViewerSettings::ScaleModeCenter);
}
+ if (parser.isSet(QStringLiteral("convert-shader-cache"))) {
+ QString fileName(parser.value(QStringLiteral("convert-shader-cache")));
+ QString cacheFileName = fileName + QStringLiteral(".shadercache");
+ QFile base64File(fileName);
+ bool success = false;
+ if (base64File.open(QIODevice::ReadOnly)) {
+ QByteArray shaderCache = QByteArray::fromBase64(base64File.readAll());
+ QFile cacheFile(cacheFileName);
+ if (cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ cacheFile.write(shaderCache);
+ success = true;
+ }
+ }
+ if (success)
+ qInfo() << "Saved shader cache to file:" << cacheFileName;
+ else
+ qWarning() << "Failed to convert" << base64File << "from base64 to a shader cache file";
+ }
if (parser.isSet(QStringLiteral("stereomode"))) {
QString stereoStr(parser.value("stereomode"));
if (stereoStr == QStringLiteral("topbottom"))