diff options
Diffstat (limited to 'src')
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, ¶mCount, &bufferSize, &length, nameBuf); + m_ProgramHandle, idx, 512, ¶mCount, &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, ¶mCount, &bufferSize, &length, nameBuf); + m_ProgramHandle, idx, 512, ¶mCount, &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, ¶mCount, &bufferSize, &length, nameBuf); + m_ProgramHandle, idx, 512, ¶mCount, &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 |