summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
34 files changed, 872 insertions, 577 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