From cd8898d92cdd483c7a34f30236b4537983e1b608 Mon Sep 17 00:00:00 2001 From: Paul Lemire Date: Wed, 7 Jun 2017 10:49:04 +0200 Subject: Introduce a GLShader class GLShader is renderer specific shader implementation for OpenGL. Shader now contains only backend information for a QShaderProgram frontend node. - Similar to the GLTexture handling, a generic adopt/abandon manager for shaders was introduced (regardless of the actually GraphicsAPIShader class). - The renderer and renderviews were adapted to the new changes. This was the last major thing preventing the modularisation of the QRenderAspect and renderers Change-Id: If671d60928b433977e9d6e5c58199827f9408a3f Task-number: QTBUG-61151 --- src/render/backend/apishadermanager_p.h | 224 +++++++++++++ src/render/backend/commandexecuter.cpp | 2 +- src/render/backend/managers_p.h | 15 + src/render/backend/render-backend.pri | 3 +- src/render/frontend/qrenderaspect.cpp | 2 +- src/render/materialsystem/materialsystem.pri | 2 - src/render/materialsystem/shader.cpp | 365 +++------------------ src/render/materialsystem/shader_p.h | 97 ++---- src/render/materialsystem/shadercache.cpp | 174 ---------- src/render/materialsystem/shaderimage.cpp | 1 - .../opengl/graphicshelpers/graphicscontext.cpp | 96 +++--- .../opengl/graphicshelpers/graphicscontext_p.h | 20 +- .../opengl/graphicshelpers/submissioncontext.cpp | 24 +- .../opengl/graphicshelpers/submissioncontext_p.h | 4 +- .../opengl/managers/glresourcemanagers.cpp | 3 + .../opengl/managers/glresourcemanagers_p.h | 12 + src/render/renderers/opengl/managers/managers.pri | 1 + src/render/renderers/opengl/renderer/glshader.cpp | 316 ++++++++++++++++++ src/render/renderers/opengl/renderer/glshader_p.h | 160 +++++++++ .../opengl/renderer/openglvertexarrayobject.cpp | 4 +- .../opengl/renderer/openglvertexarrayobject_p.h | 6 +- .../renderers/opengl/renderer/rendercommand.cpp | 8 +- .../renderers/opengl/renderer/rendercommand_p.h | 5 +- src/render/renderers/opengl/renderer/renderer.cpp | 64 +++- src/render/renderers/opengl/renderer/renderer.pri | 7 +- src/render/renderers/opengl/renderer/renderer_p.h | 6 +- .../renderers/opengl/renderer/renderview.cpp | 44 ++- .../renderers/opengl/renderer/renderview_p.h | 6 +- .../render/glshadermanager/glshadermanager.pro | 12 + .../render/glshadermanager/tst_glshadermanager.cpp | 199 +++++++++++ tests/auto/render/render.pro | 2 +- tests/auto/render/renderviews/tst_renderviews.cpp | 111 +++++-- tests/auto/render/shader/tst_shader.cpp | 46 +-- tests/auto/render/shadercache/shadercache.pro | 11 - tests/auto/render/shadercache/tst_shadercache.cpp | 293 ----------------- 35 files changed, 1265 insertions(+), 1080 deletions(-) create mode 100644 src/render/backend/apishadermanager_p.h delete mode 100644 src/render/materialsystem/shadercache.cpp create mode 100644 src/render/renderers/opengl/renderer/glshader.cpp create mode 100644 src/render/renderers/opengl/renderer/glshader_p.h create mode 100644 tests/auto/render/glshadermanager/glshadermanager.pro create mode 100644 tests/auto/render/glshadermanager/tst_glshadermanager.cpp delete mode 100644 tests/auto/render/shadercache/shadercache.pro delete mode 100644 tests/auto/render/shadercache/tst_shadercache.cpp diff --git a/src/render/backend/apishadermanager_p.h b/src/render/backend/apishadermanager_p.h new file mode 100644 index 000000000..ec17b2196 --- /dev/null +++ b/src/render/backend/apishadermanager_p.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_APISHADERMANAGER_H +#define QT3DRENDER_RENDER_APISHADERMANAGER_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Shader; + +template +class APIShaderManager +{ +public: + explicit APIShaderManager() + { + } + + ~APIShaderManager() + { + } + + QVector takeActiveResources() const + { + QReadLocker lock(&m_readWriteLock); + return m_apiShaders.keys().toVector() + m_abandonedShaders; + } + + APIShader *lookupResource(Qt3DCore::QNodeId shaderId) + { + QReadLocker lock(&m_readWriteLock); + return m_nodeIdToAPIShader.value(shaderId); + } + + // Note: automatically adopts the Shader if it needs to be created + APIShader *createOrAdoptExisting(const Shader *shader) + { + // Try to find if an APIShader that matches shader + // already exists + + { + QReadLocker readLock(&m_readWriteLock); + { + const auto end = m_apiShaders.cend(); + for (auto it = m_apiShaders.cbegin(); it != end; ++it) + if (isSameShader(it.key(), shader)) { + APIShader *apiShader = it.key(); + // Adopt if needed + readLock.unlock(); + adopt(apiShader, shader); + return apiShader; + } + } + + // Try to find if one of the scheduled for deletion APIShader + // could be reused + { + const auto end = m_abandonedShaders.end(); + for (auto it = m_abandonedShaders.begin(); it != end; ++it) + if (isSameShader(*it, shader)) { + APIShader *apiShader = *it; + // Adopt if needed + readLock.unlock(); + // Remove from list of shaders scheduled for relase + m_abandonedShaders.erase(it); + adopt(apiShader, shader); + return apiShader; + } + } + } + + // If not create one + APIShader *apiShader = create(); + adopt(apiShader, shader); + return apiShader; + } + + // Should never be called from outside code + // but left public to maintain adopt/abandon symmetry + void adopt(APIShader *apiShader, const Shader *shader) + { + QWriteLocker lock(&m_readWriteLock); + if (!m_apiShaders[apiShader].contains(shader->peerId())) { + m_apiShaders[apiShader].push_back(shader->peerId()); + m_nodeIdToAPIShader.insert(shader->peerId(), apiShader); + } + } + + void abandon(APIShader *apiShader, const Shader *shader) + { + QWriteLocker lock(&m_readWriteLock); + APIShader *storedApiShader = m_nodeIdToAPIShader.take(shader->peerId()); + Q_ASSERT(apiShader != nullptr && apiShader == storedApiShader); + + QVector &referencedShaderNodes = m_apiShaders[apiShader]; + referencedShaderNodes.removeAll(shader->peerId()); + + if (referencedShaderNodes.empty()) { + m_abandonedShaders.push_back(apiShader); + m_apiShaders.remove(apiShader); + } + } + + QVector takeAbandonned() + { + QWriteLocker lock(&m_readWriteLock); + return std::move(m_abandonedShaders); + } + + QVector takeUpdated() + { + QWriteLocker lock(&m_readWriteLock); + return std::move(m_updatedShaders); + } + + QVector shaderIdsForProgram(GLShader *glShader) const + { + QReadLocker lock(&m_readWriteLock); + return m_apiShaders.value(glShader); + } + + void purge() + { + qDeleteAll(takeAbandonned()); + } + +private: + + bool isSameShader(const APIShader *apiShader, const Shader *shaderNode) + { + const QVector nodeShaderCode = shaderNode->shaderCode(); + const QVector apiShaderCode = apiShader->shaderCode(); + + const int s = nodeShaderCode.size(); + + Q_ASSERT(s == apiShaderCode.size()); + + for (int i = 0; i < s; ++i) + if (nodeShaderCode.at(i) != apiShaderCode.at(i)) + return false; + + return true; + } + + APIShader *create() + { + APIShader *apiShader = new APIShader(); + m_updatedShaders.push_back(apiShader); + return apiShader; + } + + + QHash m_nodeIdToAPIShader; + QHash> m_apiShaders; + + QVector m_abandonedShaders; + QVector m_updatedShaders; + + mutable QReadWriteLock m_readWriteLock; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_APISHADERMANAGER_H diff --git a/src/render/backend/commandexecuter.cpp b/src/render/backend/commandexecuter.cpp index 5ed0c970d..8e45fb924 100644 --- a/src/render/backend/commandexecuter.cpp +++ b/src/render/backend/commandexecuter.cpp @@ -349,7 +349,7 @@ void CommandExecuter::performAsynchronousCommandExecution(const QVectorcommands()) { QJsonObject commandObj; Render::NodeManagers *nodeManagers = m_renderer->nodeManagers(); - commandObj.insert(QLatin1String("shader"), backendNodeToJSon(c.m_shader, nodeManagers->shaderManager())); + commandObj.insert(QLatin1String("shader"), int(c.m_shaderId.id())); commandObj.insert(QLatin1String("vao"), double(c.m_vao.handle())); commandObj.insert(QLatin1String("instanceCount"), c.m_instanceCount); commandObj.insert(QLatin1String("geometry"), backendNodeToJSon(c.m_geometry, nodeManagers->geometryManager())); diff --git a/src/render/backend/managers_p.h b/src/render/backend/managers_p.h index 024059a30..24b161b6e 100644 --- a/src/render/backend/managers_p.h +++ b/src/render/backend/managers_p.h @@ -204,6 +204,21 @@ class ShaderManager : public Qt3DCore::QResourceManager< { public: ShaderManager() {} + + // Called in AspectThread by Shader node functor destroy + void addShaderIdToCleanup(Qt3DCore::QNodeId id) + { + m_shaderIdsToCleanup.push_back(id); + } + + // Called by RenderThread in updateGLResources (locked) + QVector takeShaderIdsToCleanup() + { + return std::move(m_shaderIdsToCleanup); + } + +private: + QVector m_shaderIdsToCleanup; }; class ShaderBuilderManager : public Qt3DCore::QResourceManager< diff --git a/src/render/backend/render-backend.pri b/src/render/backend/render-backend.pri index c910adfe4..87083312f 100644 --- a/src/render/backend/render-backend.pri +++ b/src/render/backend/render-backend.pri @@ -38,7 +38,8 @@ HEADERS += \ $$PWD/visitorutils_p.h \ $$PWD/segmentsvisitor_p.h \ $$PWD/pointsvisitor_p.h \ - $$PWD/commandexecuter_p.h + $$PWD/commandexecuter_p.h \ + $$PWD/apishadermanager_p.h SOURCES += \ $$PWD/renderthread.cpp \ diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp index f25c080a0..a58b14cbd 100644 --- a/src/render/frontend/qrenderaspect.cpp +++ b/src/render/frontend/qrenderaspect.cpp @@ -299,7 +299,7 @@ void QRenderAspectPrivate::registerBackendTypes() q->registerBackendType(QSharedPointer >::create(m_renderer)); q->registerBackendType(QSharedPointer >::create(m_renderer)); q->registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers)); - q->registerBackendType(QSharedPointer >::create(m_renderer)); + q->registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->shaderManager())); q->registerBackendType(QSharedPointer >::create(m_renderer)); q->registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers)); q->registerBackendType(QSharedPointer>::create(m_renderer)); diff --git a/src/render/materialsystem/materialsystem.pri b/src/render/materialsystem/materialsystem.pri index ae3d2f78c..50e414a8e 100644 --- a/src/render/materialsystem/materialsystem.pri +++ b/src/render/materialsystem/materialsystem.pri @@ -33,7 +33,6 @@ HEADERS += \ $$PWD/technique_p.h \ $$PWD/qgraphicsapifilter.h \ $$PWD/qgraphicsapifilter_p.h \ - $$PWD/shadercache_p.h \ $$PWD/techniquemanager_p.h SOURCES += \ @@ -58,7 +57,6 @@ SOURCES += \ $$PWD/shaderimage.cpp \ $$PWD/technique.cpp \ $$PWD/qgraphicsapifilter.cpp \ - $$PWD/shadercache.cpp \ $$PWD/techniquemanager.cpp RESOURCES += \ diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp index 0d4b5edba..00d9d334f 100644 --- a/src/render/materialsystem/shader.cpp +++ b/src/render/materialsystem/shader.cpp @@ -50,6 +50,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -83,47 +84,23 @@ const int Shader::skinningPaletteNameId = StringToInt::lookupId(QLatin1String("s Shader::Shader() : BackendNode(ReadWrite) - , m_isLoaded(false) - , m_dna(0) - , m_oldDna(0) - , m_graphicsContext(nullptr) - , m_status(QShaderProgram::NotReady) , m_requiresFrontendSync(false) + , m_status(QShaderProgram::NotReady) + , m_dirty(false) { m_shaderCode.resize(static_cast(QShaderProgram::Compute) + 1); } Shader::~Shader() { - // TO DO: ShaderProgram is leaked as of now - // Fix that taking care that they may be shared given a same dna - if (m_graphicsContext) - QObject::disconnect(m_contextConnection); } void Shader::cleanup() { - // Remove this shader from the hash in the graphics context so - // nothing tries to use it after it has been recycled - { - QMutexLocker lock(&m_mutex); - if (m_graphicsContext) - m_graphicsContext->removeShaderProgramReference(this); - m_graphicsContext = nullptr; - QObject::disconnect(m_contextConnection); - } - QBackendNode::setEnabled(false); - m_isLoaded = false; - m_dna = 0; - m_oldDna = 0; - m_uniformsNames.clear(); - m_attributesNames.clear(); - m_uniformBlockNames.clear(); - m_uniforms.clear(); - m_attributes.clear(); - m_uniformBlocks.clear(); m_status = QShaderProgram::NotReady; + m_log.clear(); + m_dirty = false; } void Shader::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) @@ -146,300 +123,21 @@ void Shader::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) } } -void Shader::setGraphicsContext(GraphicsContext *context) -{ - QMutexLocker lock(&m_mutex); - m_graphicsContext = context; - if (m_graphicsContext) { - m_contextConnection = QObject::connect(m_graphicsContext->openGLContext(), - &QOpenGLContext::aboutToBeDestroyed, - [this] { setGraphicsContext(nullptr); }); - } -} - -GraphicsContext *Shader::graphicsContext() -{ - QMutexLocker lock(&m_mutex); - return m_graphicsContext; -} - -QVector Shader::uniformsNames() const -{ - return m_uniformsNames; -} - -QVector Shader::attributesNames() const -{ - return m_attributesNames; -} - -QVector Shader::uniformBlockNames() const -{ - return m_uniformBlockNames; -} - -QVector Shader::storageBlockNames() const -{ - return m_shaderStorageBlockNames; -} - -QVector Shader::shaderCode() const -{ - return m_shaderCode; -} - void Shader::setShaderCode(QShaderProgram::ShaderType type, const QByteArray &code) { if (code == m_shaderCode[type]) return; m_shaderCode[type] = code; - m_isLoaded = false; - m_status = QShaderProgram::NotReady; - updateDNA(); m_requiresFrontendSync = true; + m_dirty = true; + setStatus(QShaderProgram::NotReady); markDirty(AbstractRenderer::ShadersDirty); } -QHash Shader::activeUniformsForUniformBlock(int blockIndex) const -{ - return m_uniformBlockIndexToShaderUniforms.value(blockIndex); -} - -ShaderUniformBlock Shader::uniformBlockForBlockIndex(int blockIndex) -{ - for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { - if (m_uniformBlocks[i].m_index == blockIndex) { - return m_uniformBlocks[i]; - } - } - return ShaderUniformBlock(); -} - -ShaderUniformBlock Shader::uniformBlockForBlockNameId(int blockNameId) -{ - for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { - if (m_uniformBlocks[i].m_nameId == blockNameId) { - return m_uniformBlocks[i]; - } - } - return ShaderUniformBlock(); -} - -ShaderUniformBlock Shader::uniformBlockForBlockName(const QString &blockName) -{ - for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { - if (m_uniformBlocks[i].m_name == blockName) { - return m_uniformBlocks[i]; - } - } - return ShaderUniformBlock(); -} - -ShaderStorageBlock Shader::storageBlockForBlockIndex(int blockIndex) -{ - for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { - if (m_shaderStorageBlocks[i].m_index == blockIndex) - return m_shaderStorageBlocks[i]; - } - return ShaderStorageBlock(); -} - -ShaderStorageBlock Shader::storageBlockForBlockNameId(int blockNameId) -{ - for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { - if (m_shaderStorageBlocks[i].m_nameId == blockNameId) - return m_shaderStorageBlocks[i]; - } - return ShaderStorageBlock(); -} - -ShaderStorageBlock Shader::storageBlockForBlockName(const QString &blockName) -{ - for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { - if (m_shaderStorageBlocks[i].m_name == blockName) - return m_shaderStorageBlocks[i]; - } - return ShaderStorageBlock(); -} - -void Shader::prepareUniforms(ShaderParameterPack &pack) -{ - const PackUniformHash &values = pack.uniforms(); - - auto it = values.keys.cbegin(); - const auto end = values.keys.cend(); - - while (it != end) { - // Find if there's a uniform with the same name id - for (const ShaderUniform &uniform : qAsConst(m_uniforms)) { - if (uniform.m_nameId == *it) { - pack.setSubmissionUniform(uniform); - break; - } - } - ++it; - } -} - -void Shader::setFragOutputs(const QHash &fragOutputs) -{ - { - QMutexLocker lock(&m_mutex); - m_fragOutputs = fragOutputs; - } - updateDNA(); -} - -const QHash Shader::fragOutputs() const -{ - QMutexLocker lock(&m_mutex); - return m_fragOutputs; -} - -void Shader::updateDNA() -{ - m_oldDna = m_dna; - uint codeHash = qHash(m_shaderCode[QShaderProgram::Vertex] - + m_shaderCode[QShaderProgram::TessellationControl] - + m_shaderCode[QShaderProgram::TessellationEvaluation] - + m_shaderCode[QShaderProgram::Geometry] - + m_shaderCode[QShaderProgram::Fragment] - + m_shaderCode[QShaderProgram::Compute]); - - QMutexLocker locker(&m_mutex); - uint attachmentHash = 0; - QHash::const_iterator it = m_fragOutputs.cbegin(); - QHash::const_iterator end = m_fragOutputs.cend(); - while (it != end) { - attachmentHash += ::qHash(it.value()) + ::qHash(it.key()); - ++it; - } - const ProgramDNA newDNA = codeHash + attachmentHash; - - // Remove reference to shader based on DNA in the ShaderCache - // In turn this will allow to purge the shader program if no other - // Shader backend node references it - // Note: the purge is actually happening occasionally in GraphicsContext::beginDrawing - if (m_graphicsContext && newDNA != m_oldDna) - m_graphicsContext->removeShaderProgramReference(this); - - m_dna = newDNA; -} - -void Shader::initializeUniforms(const QVector &uniformsDescription) -{ - m_uniforms = uniformsDescription; - m_uniformsNames.resize(uniformsDescription.size()); - m_uniformsNamesIds.reserve(uniformsDescription.size()); - m_standardUniformNamesIds.reserve(5); - QHash activeUniformsInDefaultBlock; - - static const QVector standardUniformNameIds = { - modelMatrixNameId, - viewMatrixNameId, - projectionMatrixNameId, - modelViewMatrixNameId, - viewProjectionMatrixNameId, - modelViewProjectionNameId, - mvpNameId, - inverseModelMatrixNameId, - inverseViewMatrixNameId, - inverseProjectionMatrixNameId, - inverseModelViewNameId, - inverseViewProjectionMatrixNameId, - inverseModelViewProjectionNameId, - modelNormalMatrixNameId, - modelViewNormalNameId, - viewportMatrixNameId, - inverseViewportMatrixNameId, - aspectRatioNameId, - exposureNameId, - gammaNameId, - timeNameId, - eyePositionNameId, - skinningPaletteNameId, - }; - - for (int i = 0, m = uniformsDescription.size(); i < m; i++) { - m_uniformsNames[i] = m_uniforms[i].m_name; - const int nameId = StringToInt::lookupId(m_uniformsNames[i]); - m_uniforms[i].m_nameId = nameId; - - // Is the uniform a Qt3D "Standard" uniform or a user defined one? - if (standardUniformNameIds.contains(nameId)) - m_standardUniformNamesIds.push_back(nameId); - else - m_uniformsNamesIds.push_back(nameId); - - if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block - qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex; - activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]); - } - } - m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock); -} - -void Shader::initializeAttributes(const QVector &attributesDescription) -{ - m_attributes = attributesDescription; - m_attributesNames.resize(attributesDescription.size()); - m_attributeNamesIds.resize(attributesDescription.size()); - for (int i = 0, m = attributesDescription.size(); i < m; i++) { - m_attributesNames[i] = attributesDescription[i].m_name; - m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]); - m_attributeNamesIds[i] = m_attributes[i].m_nameId; - qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name; - } -} - -void Shader::initializeUniformBlocks(const QVector &uniformBlockDescription) -{ - m_uniformBlocks = uniformBlockDescription; - m_uniformBlockNames.resize(uniformBlockDescription.size()); - m_uniformBlockNamesIds.resize(uniformBlockDescription.size()); - for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) { - m_uniformBlockNames[i] = m_uniformBlocks[i].m_name; - m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]); - m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i]; - qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}"; - - // Find all active uniforms for the shader block - QVector::const_iterator uniformsIt = m_uniforms.cbegin(); - const QVector::const_iterator uniformsEnd = m_uniforms.cend(); - - QVector::const_iterator uniformNamesIt = m_uniformsNames.cbegin(); - const QVector::const_iterator uniformNamesEnd = m_attributesNames.cend(); - - QHash activeUniformsInBlock; - - while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) { - if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) { - QString uniformName = *uniformNamesIt; - if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(m_uniformBlockNames[i])) - uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt; - activeUniformsInBlock.insert(uniformName, *uniformsIt); - qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex; - } - ++uniformsIt; - ++uniformNamesIt; - } - m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock); - } -} - -void Shader::initializeShaderStorageBlocks(const QVector &shaderStorageBlockDescription) +QVector Shader::shaderCode() const { - m_shaderStorageBlocks = shaderStorageBlockDescription; - m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size()); - m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size()); - - for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) { - m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name; - m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]); - m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i]; - qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}"; - } + return m_shaderCode; } /*! @@ -449,26 +147,11 @@ void Shader::initializeShaderStorageBlocks(const QVector &sh */ void Shader::initializeFromReference(const Shader &other) { - Q_ASSERT(m_dna == other.m_dna); - m_uniformsNamesIds = other.m_uniformsNamesIds; - m_standardUniformNamesIds = other.m_standardUniformNamesIds; - m_uniformsNames = other.m_uniformsNames; - m_uniforms = other.m_uniforms; - m_attributesNames = other.m_attributesNames; - m_attributeNamesIds = other.m_attributeNamesIds; - m_attributes = other.m_attributes; - m_uniformBlockNamesIds = other.m_uniformBlockNamesIds; - m_uniformBlockNames = other.m_uniformBlockNames; - m_uniformBlocks = other.m_uniformBlocks; - m_uniformBlockIndexToShaderUniforms = other.m_uniformBlockIndexToShaderUniforms; - m_fragOutputs = other.m_fragOutputs; - m_shaderStorageBlockNamesIds = other.m_shaderStorageBlockNamesIds; - m_shaderStorageBlockNames = other.m_shaderStorageBlockNames; - m_shaderStorageBlocks = other.m_shaderStorageBlocks; - m_isLoaded = other.m_isLoaded; m_status = other.m_status; m_log = other.m_log; m_requiresFrontendSync = true; + setStatus(other.status()); + setLog(other.log()); } void Shader::setLog(const QString &log) @@ -483,6 +166,32 @@ void Shader::setStatus(QShaderProgram::Status status) m_requiresFrontendSync = true; } +ShaderFunctor::ShaderFunctor(AbstractRenderer *renderer, ShaderManager *manager) + : m_renderer(renderer) + , m_shaderManager(manager) +{ +} + +QBackendNode *ShaderFunctor::create(const QNodeCreatedChangeBasePtr &change) const +{ + Shader *backend = m_shaderManager->getOrCreateResource(change->subjectId()); + backend->setRenderer(m_renderer); + return backend; +} + +QBackendNode *ShaderFunctor::get(QNodeId id) const +{ + return m_shaderManager->lookupResource(id); +} + +void ShaderFunctor::destroy(QNodeId id) const +{ + m_shaderManager->addShaderIdToCleanup(id); + // We only add ourselves to the dirty list + // The actual removal needs to be performed after we have + // destroyed the associated APIShader in the RenderThread +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/materialsystem/shader_p.h b/src/render/materialsystem/shader_p.h index 4c5bc5ea1..301976065 100644 --- a/src/render/materialsystem/shader_p.h +++ b/src/render/materialsystem/shader_p.h @@ -52,11 +52,8 @@ // #include -#include -#include -#include #include -#include +#include #include QT_BEGIN_NAMESPACE @@ -70,8 +67,6 @@ namespace Render { class ShaderManager; class AttachmentPack; -typedef uint ProgramDNA; - class Q_AUTOTEST_EXPORT Shader : public BackendNode { public: @@ -104,92 +99,28 @@ public: void cleanup(); - void setGraphicsContext(GraphicsContext *context); - GraphicsContext *graphicsContext(); - - void prepareUniforms(ShaderParameterPack &pack); - void setFragOutputs(const QHash &fragOutputs); - const QHash fragOutputs() const; - - inline QVector uniformsNamesIds() const { return m_uniformsNamesIds; } - inline QVector standardUniformNameIds() const { return m_standardUniformNamesIds; } - inline QVector uniformBlockNamesIds() const { return m_uniformBlockNamesIds; } - inline QVector storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; } - inline QVector attributeNamesIds() const { return m_attributeNamesIds; } - - QVector uniformsNames() const; - QVector attributesNames() const; - QVector uniformBlockNames() const; - QVector storageBlockNames() const; QVector shaderCode() const; void setShaderCode(QShaderProgram::ShaderType type, const QByteArray &code); - void syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime) override; - bool isLoaded() const { QMutexLocker lock(&m_mutex); return m_isLoaded; } - void setLoaded(bool loaded) { QMutexLocker lock(&m_mutex); m_isLoaded = loaded; } - ProgramDNA dna() const Q_DECL_NOTHROW { return m_dna; } - - inline QVector uniforms() const { return m_uniforms; } - inline QVector attributes() const { return m_attributes; } - inline QVector uniformBlocks() const { return m_uniformBlocks; } - inline QVector storageBlocks() const { return m_shaderStorageBlocks; } - - QHash activeUniformsForUniformBlock(int blockIndex) const; - - ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId); - ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex); - ShaderUniformBlock uniformBlockForBlockName(const QString &blockName); - - ShaderStorageBlock storageBlockForBlockIndex(int blockIndex); - ShaderStorageBlock storageBlockForBlockNameId(int blockNameId); - ShaderStorageBlock storageBlockForBlockName(const QString &blockName); + void syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime); inline QString log() const { return m_log; } inline QShaderProgram::Status status() const { return m_status; } + bool isDirty() const { return m_dirty; } + void unsetDirty() { m_dirty = false; } inline bool requiresFrontendSync() const { return m_requiresFrontendSync; } inline void unsetRequiresFrontendSync() { m_requiresFrontendSync = false; } private: - QVector m_uniformsNames; - QVector m_uniformsNamesIds; - QVector m_standardUniformNamesIds; - QVector m_uniforms; - - QVector m_attributesNames; - QVector m_attributeNamesIds; - QVector m_attributes; - - QVector m_uniformBlockNames; - QVector m_uniformBlockNamesIds; - QVector m_uniformBlocks; - QHash > m_uniformBlockIndexToShaderUniforms; - - QVector m_shaderStorageBlockNames; - QVector m_shaderStorageBlockNamesIds; - QVector m_shaderStorageBlocks; - - QHash m_fragOutputs; - QVector m_shaderCode; - bool m_isLoaded; - ProgramDNA m_dna; - ProgramDNA m_oldDna; - mutable QMutex m_mutex; - GraphicsContext *m_graphicsContext; - QMetaObject::Connection m_contextConnection; QString m_log; QShaderProgram::Status m_status; bool m_requiresFrontendSync; + bool m_dirty; - void updateDNA(); - - // Private so that only GraphicContext can call it - void initializeUniforms(const QVector &uniformsDescription); - void initializeAttributes(const QVector &attributesDescription); - void initializeUniformBlocks(const QVector &uniformBlockDescription); - void initializeShaderStorageBlocks(const QVector &shaderStorageBlockDescription); + QVector m_pendingNotifications; void initializeFromReference(const Shader &other); void setLog(const QString &log); @@ -202,11 +133,25 @@ private: inline QDebug operator<<(QDebug dbg, const Shader &shader) { QDebugStateSaver saver(dbg); - dbg << "QNodeId =" << shader.peerId() << "dna =" << shader.dna() << Qt::endl; + dbg << "QNodeId =" << shader.peerId() << Qt::endl; return dbg; } #endif +class ShaderFunctor : public Qt3DCore::QBackendNodeMapper +{ +public: + explicit ShaderFunctor(AbstractRenderer *renderer, + ShaderManager *manager); + Qt3DCore::QBackendNode *create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const final; + Qt3DCore::QBackendNode *get(Qt3DCore::QNodeId id) const final; + void destroy(Qt3DCore::QNodeId id) const final; + +private: + AbstractRenderer *m_renderer; + ShaderManager *m_shaderManager; +}; + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/materialsystem/shadercache.cpp b/src/render/materialsystem/shadercache.cpp deleted file mode 100644 index 78193755f..000000000 --- a/src/render/materialsystem/shadercache.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt3D module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "shadercache_p.h" - -#include - -QT_BEGIN_NAMESPACE - -namespace Qt3DRender { -namespace Render { - -/*! - * \internal - * - * Destroys the ShaderCache and deletes all cached QOpenGLShaderPrograms - */ -ShaderCache::~ShaderCache() -{ - qDeleteAll(m_programHash); -} - -/*! - * \internal - * - * Looks up the QOpenGLShaderProgram corresponding to \a dna. Also checks to see if the cache - * contains a reference to this shader program from the Shader with peerId of \a shaderPeerId. - * If there is no existing reference for \a shaderPeerId, one is recorded. - * - * \return A pointer to the shader program if it is cached, nullptr otherwise - */ -QOpenGLShaderProgram *ShaderCache::getShaderProgramAndAddRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, bool *wasPresent) -{ - auto shaderProgram = m_programHash.constFind(dna); - - // Some callers may wish to differentiate between a result of null due to - // not having anything in the cache and a result of null due to the cache - // containing a program the shaders of which failed to compile. - if (wasPresent) - *wasPresent = shaderProgram != m_programHash.constEnd(); - - if (shaderProgram != m_programHash.constEnd()) { - // Ensure we store the fact that shaderPeerId references this shader - QMutexLocker lock(&m_refsMutex); - QVector &programRefs = m_programRefs[dna]; - auto it = std::lower_bound(programRefs.begin(), programRefs.end(), shaderPeerId); - if (*it != shaderPeerId) - programRefs.insert(it, shaderPeerId); - - m_pendingRemoval.removeOne(dna); - - return *shaderProgram; - } - - return nullptr; -} - -/*! - * \internal - * - * Inserts the \a program in the cache indexed by \a dna and adds a reference to it from - * \a shaderPeerId. The cache takes ownership of the \a program. - */ -void ShaderCache::insert(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, QOpenGLShaderProgram *program) -{ - Q_ASSERT(!m_programHash.contains(dna)); - m_programHash.insert(dna, program); - QMutexLocker lock(&m_refsMutex); - Q_ASSERT(!m_programRefs.contains(dna)); - QVector programRefs; - programRefs.push_back(shaderPeerId); - m_programRefs.insert(dna, programRefs); -} - -/*! - * \internal - * - * Removes a reference to the shader program indexed by \a dna from the Shader with peerId of - * \a shaderPeerId. If this was the last reference, the dna and corresponding shader are added - * to a list of items to be removed by the next call to purge. - */ -void ShaderCache::removeRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId) -{ - QMutexLocker lock(&m_refsMutex); - auto it = m_programRefs.find(dna); - if (it != m_programRefs.end()) { - QVector &programRefs = it.value(); - programRefs.removeOne(shaderPeerId); - if (programRefs.isEmpty()) - m_pendingRemoval.append(dna); - } -} - -/*! - * \internal - * - * Iterates through a list of program dna's and checks to see if they still have no references - * from Shaders. If so, the dna and corresponding QOpenGLShaderProgram are removed from the cache. - */ -void ShaderCache::purge() -{ - QMutexLocker lock(&m_refsMutex); - for (const ProgramDNA &dna : qAsConst(m_pendingRemoval)) { - QVector &programRefs = m_programRefs[dna]; - if (programRefs.isEmpty()) { - delete m_programHash.take(dna); - m_programRefs.remove(dna); - } - } - - m_pendingRemoval.clear(); -} - -/*! - * \internal - * - * Deletes all cached shader programs and removes any references - */ -void ShaderCache::clear() -{ - QMutexLocker lock(&m_refsMutex); - qDeleteAll(m_programHash); - m_programHash.clear(); - m_programRefs.clear(); - m_pendingRemoval.clear(); -} - -QOpenGLShaderProgram *ShaderCache::getShaderProgramForDNA(ProgramDNA dna) const -{ - return m_programHash.value(dna, nullptr); -} - -QVector ShaderCache::shaderIdsForProgram(ProgramDNA dna) const -{ - return m_programRefs.value(dna); -} - -} // namespace Render -} // namespace Qt3DRender - -QT_END_NAMESPACE diff --git a/src/render/materialsystem/shaderimage.cpp b/src/render/materialsystem/shaderimage.cpp index 65a4cf761..59a06ca9c 100644 --- a/src/render/materialsystem/shaderimage.cpp +++ b/src/render/materialsystem/shaderimage.cpp @@ -103,7 +103,6 @@ void ShaderImage::syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTi m_format = node->format(); markDirty(AbstractRenderer::ParameterDirty); } - if (node->access() != m_access) { m_access = node->access(); markDirty(AbstractRenderer::ParameterDirty); diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp index 9e5b3e158..e971897fb 100644 --- a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #if !defined(QT_OPENGL_ES_2) @@ -133,7 +134,6 @@ GraphicsContext::GraphicsContext() , m_defaultFBO(0) , m_gl(nullptr) , m_glHelper(nullptr) - , m_shaderCache(nullptr) , m_debugLogger(nullptr) , m_currentVAO(nullptr) { @@ -153,7 +153,7 @@ void GraphicsContext::initialize() { m_initialized = true; - Q_ASSERT(m_gl && m_shaderCache); + Q_ASSERT(m_gl); m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &m_maxTextureUnits); qCDebug(Backend) << "context supports" << m_maxTextureUnits << "texture units"; @@ -231,12 +231,13 @@ void GraphicsContext::doneCurrent() } // Called by GL Command Thread -QOpenGLShaderProgram *GraphicsContext::createShaderProgram(Shader *shaderNode) +GraphicsContext::ShaderCreationInfo GraphicsContext::createShaderProgram(GLShader *shader) { - QScopedPointer shaderProgram(new QOpenGLShaderProgram); + QOpenGLShaderProgram *shaderProgram = shader->shaderProgram(); // Compile shaders - const auto shaderCode = shaderNode->shaderCode(); + const auto shaderCode = shader->shaderCode(); + QString logs; for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) { const QShaderProgram::ShaderType type = static_cast(i); @@ -251,25 +252,24 @@ QOpenGLShaderProgram *GraphicsContext::createShaderProgram(Shader *shaderNode) // Call glBindFragDataLocation and link the program // Since we are sharing shaders in the backend, we assume that if using custom // fragOutputs, they should all be the same for a given shader - bindFragOutputs(shaderProgram->programId(), shaderNode->fragOutputs()); + bindFragOutputs(shaderProgram->programId(), shader->fragOutputs()); const bool linkSucceeded = shaderProgram->link(); logs += shaderProgram->log(); - shaderNode->setLog(logs); - shaderNode->setStatus(linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); - if (!linkSucceeded) - return nullptr; + // Perform shader introspection + introspectShaderInterface(shader); - // take from scoped-pointer so it doesn't get deleted - return shaderProgram.take(); + return {linkSucceeded, logs}; } // That assumes that the shaderProgram in Shader stays the same -void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram) +void GraphicsContext::introspectShaderInterface(GLShader *shader) { - shader->initializeUniforms(m_glHelper->programUniformsAndLocations(shaderProgram->programId())); - shader->initializeAttributes(m_glHelper->programAttributesAndLocations(shaderProgram->programId())); + QOpenGLShaderProgram *shaderProgram = shader->shaderProgram(); + GraphicsHelperInterface *glHelper = resolveHighestOpenGLFunctions(); + shader->initializeUniforms(glHelper->programUniformsAndLocations(shaderProgram->programId())); + shader->initializeAttributes(glHelper->programAttributesAndLocations(shaderProgram->programId())); if (m_glHelper->supportsFeature(GraphicsHelperInterface::UniformBufferObject)) shader->initializeUniformBlocks(m_glHelper->programUniformBlocks(shaderProgram->programId())); if (m_glHelper->supportsFeature(GraphicsHelperInterface::ShaderStorageObject)) @@ -278,51 +278,47 @@ void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderPro // Called by Renderer::updateGLResources -void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager) +void GraphicsContext::loadShader(Shader *shaderNode, + ShaderManager *shaderManager, + GLShaderManager *glShaderManager) { - bool wasPresent = false; - QOpenGLShaderProgram *shaderProgram = m_shaderCache->getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent); - if (!shaderProgram && !wasPresent) { - // No matching QOpenGLShader in the cache so create one - shaderProgram = createShaderProgram(shader); + const Qt3DCore::QNodeId shaderId = shaderNode->peerId(); + GLShader *glShader = glShaderManager->lookupResource(shaderId); - // Store in cache (even when failed and shaderProgram is null) - m_shaderCache->insert(shader->dna(), shader->peerId(), shaderProgram); + // We already have a shader associated with the node + if (glShader != nullptr) { + // We need to abandon it + glShaderManager->abandon(glShader, shaderNode); } - // Ensure the Shader node knows about the program interface - if (Q_LIKELY(shaderProgram != nullptr) && !shader->isLoaded()) { - + // We create or adopt an already created glShader + glShader = glShaderManager->createOrAdoptExisting(shaderNode); + + const QVector sharedShaderIds = glShaderManager->shaderIdsForProgram(glShader); + if (sharedShaderIds.size() == 1) { + // Shader in the cache hasn't been loaded yet + glShader->setGraphicsContext(this); + glShader->setShaderCode(shaderNode->shaderCode()); + const ShaderCreationInfo loadResult = createShaderProgram(glShader); + shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); + shaderNode->setLog(loadResult.logs); + // Loaded in the sense we tried to load it (and maybe it failed) + glShader->setLoaded(true); + } else { // Find an already loaded shader that shares the same QOpenGLShaderProgram - Shader *refShader = nullptr; - const QVector sharedShaderIds = m_shaderCache->shaderIdsForProgram(shader->dna()); for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) { - Shader *sharedShader = manager->lookupResource(sharedShaderId); - // Note: no need to check if shader->peerId != sharedShader->peerId - // as we are sure that this code path is executed only if !shared->isLoaded - if (sharedShader->isLoaded()) { - refShader = sharedShader; + if (sharedShaderId != shaderNode->peerId()) { + Shader *refShader = shaderManager->lookupResource(sharedShaderId); + // We only introspect once per actual OpenGL shader program + // rather than once per ShaderNode. + shaderNode->initializeFromReference(*refShader); break; } } - - // We only introspect once per actual OpenGL shader program - // rather than once per ShaderNode. - if (refShader != nullptr) - shader->initializeFromReference(*refShader); - else // Introspect and set up interface description on Shader backend node - introspectShaderInterface(shader, shaderProgram); - - shader->setGraphicsContext(this); - shader->setLoaded(true); - // Will force notifications to be sent to frontend at next frame - shader->markDirty(AbstractRenderer::ShadersDirty); } -} - -void GraphicsContext::removeShaderProgramReference(Shader *shaderNode) -{ - m_shaderCache->removeRef(shaderNode->dna(), shaderNode->peerId()); + shaderNode->unsetDirty(); + // Ensure we will rebuilt material caches + shaderNode->markDirty(AbstractRenderer::AllDirty); } void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments) diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h index b4f589e71..d3f3615d5 100644 --- a/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h @@ -68,7 +68,6 @@ #include #include #include -#include #include #include #include @@ -89,6 +88,8 @@ class GraphicsHelperInterface; class RenderTarget; class AttachmentPack; class ShaderManager; +class GLShader; +class GLShaderManager; typedef QPair NamedUniformLocation; @@ -98,9 +99,6 @@ public: GraphicsContext(); ~GraphicsContext(); - void setShaderCache(ShaderCache *shaderCache) { m_shaderCache = shaderCache; } - ShaderCache *shaderCache() const { return m_shaderCache; } - void setOpenGLContext(QOpenGLContext* ctx); QOpenGLContext *openGLContext() { return m_gl; } bool makeCurrent(QSurface *surface); @@ -109,10 +107,15 @@ public: bool isInitialized() const; // Shaders - QOpenGLShaderProgram *createShaderProgram(Shader *shaderNode); - void introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram); - void loadShader(Shader* shader, ShaderManager *manager); - void removeShaderProgramReference(Shader *shaderNode); + struct ShaderCreationInfo + { + bool linkSucceeded = false; + QString logs; + }; + + ShaderCreationInfo createShaderProgram(GLShader *shaderNode); + void introspectShaderInterface(GLShader *shader); + void loadShader(Shader* shader, ShaderManager *shaderManager, GLShaderManager *glShaderManager); GLuint defaultFBO() const { return m_defaultFBO; } @@ -195,7 +198,6 @@ public: QHash m_glHelpers; GraphicsApiFilterData m_contextInfo; - ShaderCache *m_shaderCache; QScopedPointer m_debugLogger; friend class OpenGLVertexArrayObject; diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp index 3a619f361..32852217b 100644 --- a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -363,7 +364,6 @@ SubmissionContext::SubmissionContext() , m_id(nextFreeContextId()) , m_surface(nullptr) , m_activeShader(nullptr) - , m_activeShaderDNA(0) , m_renderTargetFormat(QAbstractTexture::NoFormat) , m_currClearStencilValue(0) , m_currClearDepthValue(1.f) @@ -460,17 +460,9 @@ bool SubmissionContext::beginDrawing(QSurface *surface) if (m_activeShader) { m_activeShader = nullptr; - m_activeShaderDNA = 0; } m_boundArrayBuffer = nullptr; - - static int callCount = 0; - ++callCount; - const int shaderPurgePeriod = 600; - if (callCount % shaderPurgePeriod == 0) - m_shaderCache->purge(); - return true; } @@ -766,7 +758,6 @@ void SubmissionContext::setViewport(const QRectF &viewport, const QSize &surface void SubmissionContext::releaseOpenGL() { - m_shaderCache->clear(); m_renderBufferHash.clear(); // Stop and destroy the OpenGL logger @@ -779,27 +770,25 @@ void SubmissionContext::releaseOpenGL() // The OpenGLContext is not current on any surface at this point void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx) { - Q_ASSERT(ctx && m_shaderCache); + Q_ASSERT(ctx); releaseOpenGL(); m_gl = ctx; } // Called only from RenderThread -bool SubmissionContext::activateShader(ProgramDNA shaderDNA) +bool SubmissionContext::activateShader(GLShader *shader) { - if (shaderDNA != m_activeShaderDNA) { + if (shader->shaderProgram() != m_activeShader) { // Ensure material uniforms are re-applied m_material = nullptr; - m_activeShader = m_shaderCache->getShaderProgramForDNA(shaderDNA); + m_activeShader = shader->shaderProgram(); if (Q_LIKELY(m_activeShader != nullptr)) { m_activeShader->bind(); - m_activeShaderDNA = shaderDNA; } else { m_glHelper->useProgram(0); - qCWarning(Backend) << "No shader program found for DNA"; - m_activeShaderDNA = 0; + qWarning() << "No shader program found"; return false; } } @@ -1334,6 +1323,7 @@ void SubmissionContext::specifyAttribute(const Attribute *attribute, Q_UNREACHABLE(); } + Q_ASSERT(!glBufferHandle.isNull()); VAOVertexAttribute attr; attr.bufferHandle = glBufferHandle; attr.attributeType = attributeType; diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h index a8700dd3a..d2121a3b7 100644 --- a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h @@ -60,7 +60,6 @@ #include #include #include -#include #include QT_BEGIN_NAMESPACE @@ -105,7 +104,7 @@ public: QRectF viewport() const { return m_viewport; } // Shaders - bool activateShader(ProgramDNA shaderDNA); + bool activateShader(GLShader *shader); QOpenGLShaderProgram *activeShader() const { return m_activeShader; } // FBO @@ -183,7 +182,6 @@ private: QSize m_surfaceSize; QOpenGLShaderProgram *m_activeShader; - ProgramDNA m_activeShaderDNA; QHash m_renderBufferHash; QHash m_renderTargets; diff --git a/src/render/renderers/opengl/managers/glresourcemanagers.cpp b/src/render/renderers/opengl/managers/glresourcemanagers.cpp index e3be4c7af..2b1b87925 100644 --- a/src/render/renderers/opengl/managers/glresourcemanagers.cpp +++ b/src/render/renderers/opengl/managers/glresourcemanagers.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "glresourcemanagers_p.h" +#include #include QT_BEGIN_NAMESPACE @@ -49,6 +50,7 @@ namespace Render { GLResourceManagers::GLResourceManagers() : m_glBufferManager(new GLBufferManager()) + , m_glShaderManager(new GLShaderManager()) , m_glTextureManager(new GLTextureManager()) , m_glFenceManager(new GLFenceManager()) , m_vaoManager(new VAOManager()) @@ -60,6 +62,7 @@ GLResourceManagers::~GLResourceManagers() delete m_vaoManager; delete m_glFenceManager; delete m_glTextureManager; + delete m_glShaderManager; delete m_glBufferManager; } diff --git a/src/render/renderers/opengl/managers/glresourcemanagers_p.h b/src/render/renderers/opengl/managers/glresourcemanagers_p.h index a2db09316..8c4c46bca 100644 --- a/src/render/renderers/opengl/managers/glresourcemanagers_p.h +++ b/src/render/renderers/opengl/managers/glresourcemanagers_p.h @@ -57,6 +57,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -93,6 +95,14 @@ public: QHash texNodeIdForGLTexture; }; +class Q_AUTOTEST_EXPORT GLShaderManager : public APIShaderManager +{ +public: + explicit GLShaderManager() + : APIShaderManager() + {} +}; + class Q_AUTOTEST_EXPORT GLResourceManagers { @@ -101,12 +111,14 @@ public: ~GLResourceManagers(); inline VAOManager *vaoManager() const noexcept { return m_vaoManager; } + inline GLShaderManager *glShaderManager() const noexcept { return m_glShaderManager; } inline GLTextureManager *glTextureManager() const noexcept { return m_glTextureManager; } inline GLBufferManager *glBufferManager() const noexcept { return m_glBufferManager; } inline GLFenceManager *glFenceManager() const noexcept { return m_glFenceManager; } private: GLBufferManager *m_glBufferManager; + GLShaderManager *m_glShaderManager; GLTextureManager *m_glTextureManager; GLFenceManager *m_glFenceManager; VAOManager *m_vaoManager; diff --git a/src/render/renderers/opengl/managers/managers.pri b/src/render/renderers/opengl/managers/managers.pri index 97a4c2c45..56a0732b1 100644 --- a/src/render/renderers/opengl/managers/managers.pri +++ b/src/render/renderers/opengl/managers/managers.pri @@ -3,6 +3,7 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/gl_handle_types_p.h \ $$PWD/glresourcemanagers_p.h + $$PWD/glshadermanager_p.h \ SOURCES += \ $$PWD/glresourcemanagers.cpp diff --git a/src/render/renderers/opengl/renderer/glshader.cpp b/src/render/renderers/opengl/renderer/glshader.cpp new file mode 100644 index 000000000..a6f118d65 --- /dev/null +++ b/src/render/renderers/opengl/renderer/glshader.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "glshader_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +GLShader::GLShader() + : m_isLoaded(false) + , m_graphicsContext(nullptr) +{ + m_shaderCode.resize(static_cast(QShaderProgram::Compute) + 1); +} + +void GLShader::setGraphicsContext(GraphicsContext *context) +{ + QMutexLocker lock(&m_mutex); + m_graphicsContext = context; + if (m_graphicsContext) { + m_contextConnection = QObject::connect(m_graphicsContext->openGLContext(), + &QOpenGLContext::aboutToBeDestroyed, + [this] { setGraphicsContext(nullptr); }); + } +} + +GraphicsContext *GLShader::graphicsContext() +{ + QMutexLocker lock(&m_mutex); + return m_graphicsContext; +} + + +QVector GLShader::uniformsNames() const +{ + return m_uniformsNames; +} + +QVector GLShader::attributesNames() const +{ + return m_attributesNames; +} + +QVector GLShader::uniformBlockNames() const +{ + return m_uniformBlockNames; +} + +QVector GLShader::storageBlockNames() const +{ + return m_shaderStorageBlockNames; +} + +QVector GLShader::shaderCode() const +{ + return m_shaderCode; +} + +QHash GLShader::activeUniformsForUniformBlock(int blockIndex) const +{ + return m_uniformBlockIndexToShaderUniforms.value(blockIndex); +} + +ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) +{ + for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { + if (m_uniformBlocks[i].m_index == blockIndex) { + return m_uniformBlocks[i]; + } + } + return ShaderUniformBlock(); +} + +ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) +{ + for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { + if (m_uniformBlocks[i].m_nameId == blockNameId) { + return m_uniformBlocks[i]; + } + } + return ShaderUniformBlock(); +} + +ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) +{ + for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { + if (m_uniformBlocks[i].m_name == blockName) { + return m_uniformBlocks[i]; + } + } + return ShaderUniformBlock(); +} + +ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) +{ + for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { + if (m_shaderStorageBlocks[i].m_index == blockIndex) + return m_shaderStorageBlocks[i]; + } + return ShaderStorageBlock(); +} + +ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) +{ + for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { + if (m_shaderStorageBlocks[i].m_nameId == blockNameId) + return m_shaderStorageBlocks[i]; + } + return ShaderStorageBlock(); +} + +ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) +{ + for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { + if (m_shaderStorageBlocks[i].m_name == blockName) + return m_shaderStorageBlocks[i]; + } + return ShaderStorageBlock(); +} + +void GLShader::prepareUniforms(ShaderParameterPack &pack) +{ + const PackUniformHash &values = pack.uniforms(); + + auto it = values.keys.cbegin(); + const auto end = values.keys.cend(); + + while (it != end) { + // Find if there's a uniform with the same name id + for (const ShaderUniform &uniform : qAsConst(m_uniforms)) { + if (uniform.m_nameId == *it) { + pack.setSubmissionUniform(uniform); + break; + } + } + ++it; + } +} + +void GLShader::setFragOutputs(const QHash &fragOutputs) +{ + { + QMutexLocker lock(&m_mutex); + m_fragOutputs = fragOutputs; + } +// updateDNA(); +} + +const QHash GLShader::fragOutputs() const +{ + QMutexLocker lock(&m_mutex); + return m_fragOutputs; +} + +void GLShader::initializeUniforms(const QVector &uniformsDescription) +{ + m_uniforms = uniformsDescription; + m_uniformsNames.resize(uniformsDescription.size()); + m_uniformsNamesIds.reserve(uniformsDescription.size()); + m_standardUniformNamesIds.reserve(5); + QHash activeUniformsInDefaultBlock; + + static const QVector standardUniformNameIds = { + Shader::modelMatrixNameId, + Shader::viewMatrixNameId, + Shader::projectionMatrixNameId, + Shader::modelViewMatrixNameId, + Shader::viewProjectionMatrixNameId, + Shader::modelViewProjectionNameId, + Shader::mvpNameId, + Shader::inverseModelMatrixNameId, + Shader::inverseViewMatrixNameId, + Shader::inverseProjectionMatrixNameId, + Shader::inverseModelViewNameId, + Shader::inverseViewProjectionMatrixNameId, + Shader::inverseModelViewProjectionNameId, + Shader::modelNormalMatrixNameId, + Shader::modelViewNormalNameId, + Shader::viewportMatrixNameId, + Shader::inverseViewportMatrixNameId, + Shader::aspectRatioNameId, + Shader::exposureNameId, + Shader::gammaNameId, + Shader::timeNameId, + Shader::eyePositionNameId, + Shader::skinningPaletteNameId, + }; + + for (int i = 0, m = uniformsDescription.size(); i < m; i++) { + m_uniformsNames[i] = m_uniforms[i].m_name; + const int nameId = StringToInt::lookupId(m_uniformsNames[i]); + m_uniforms[i].m_nameId = nameId; + + // Is the uniform a Qt3D "Standard" uniform or a user defined one? + if (standardUniformNameIds.contains(nameId)) + m_standardUniformNamesIds.push_back(nameId); + else + m_uniformsNamesIds.push_back(nameId); + + if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block + qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex; + activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]); + } + } + m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock); +} + +void GLShader::initializeAttributes(const QVector &attributesDescription) +{ + m_attributes = attributesDescription; + m_attributesNames.resize(attributesDescription.size()); + m_attributeNamesIds.resize(attributesDescription.size()); + for (int i = 0, m = attributesDescription.size(); i < m; i++) { + m_attributesNames[i] = attributesDescription[i].m_name; + m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]); + m_attributeNamesIds[i] = m_attributes[i].m_nameId; + qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name; + } +} + +void GLShader::initializeUniformBlocks(const QVector &uniformBlockDescription) +{ + m_uniformBlocks = uniformBlockDescription; + m_uniformBlockNames.resize(uniformBlockDescription.size()); + m_uniformBlockNamesIds.resize(uniformBlockDescription.size()); + for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) { + m_uniformBlockNames[i] = m_uniformBlocks[i].m_name; + m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]); + m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i]; + qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}"; + + // Find all active uniforms for the shader block + QVector::const_iterator uniformsIt = m_uniforms.cbegin(); + const QVector::const_iterator uniformsEnd = m_uniforms.cend(); + + QVector::const_iterator uniformNamesIt = m_uniformsNames.cbegin(); + const QVector::const_iterator uniformNamesEnd = m_attributesNames.cend(); + + QHash activeUniformsInBlock; + + while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) { + if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) { + QString uniformName = *uniformNamesIt; + if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(m_uniformBlockNames[i])) + uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt; + activeUniformsInBlock.insert(uniformName, *uniformsIt); + qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex; + } + ++uniformsIt; + ++uniformNamesIt; + } + m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock); + } +} + +void GLShader::initializeShaderStorageBlocks(const QVector &shaderStorageBlockDescription) +{ + m_shaderStorageBlocks = shaderStorageBlockDescription; + m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size()); + m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size()); + + for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) { + m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name; + m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]); + m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i]; + qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}"; + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/glshader_p.h b/src/render/renderers/opengl/renderer/glshader_p.h new file mode 100644 index 000000000..dec085291 --- /dev/null +++ b/src/render/renderers/opengl/renderer/glshader_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GLSHADER_P_H +#define QT3DRENDER_RENDER_GLSHADER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; + +namespace Qt3DRender { + +namespace Render { + +class Q_AUTOTEST_EXPORT GLShader +{ +public: + GLShader(); + + void setGraphicsContext(GraphicsContext *context); + GraphicsContext *graphicsContext(); + + bool isLoaded() const { return m_isLoaded; } + void setLoaded(bool loaded) { m_isLoaded = loaded; } + + void prepareUniforms(ShaderParameterPack &pack); + + void setFragOutputs(const QHash &fragOutputs); + const QHash fragOutputs() const; + + inline QVector uniformsNamesIds() const { return m_uniformsNamesIds; } + inline QVector standardUniformNameIds() const { return m_standardUniformNamesIds; } + inline QVector uniformBlockNamesIds() const { return m_uniformBlockNamesIds; } + inline QVector storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; } + inline QVector attributeNamesIds() const { return m_attributeNamesIds; } + + QVector uniformsNames() const; + QVector attributesNames() const; + QVector uniformBlockNames() const; + QVector storageBlockNames() const; + + inline QVector uniforms() const { return m_uniforms; } + inline QVector attributes() const { return m_attributes; } + inline QVector uniformBlocks() const { return m_uniformBlocks; } + inline QVector storageBlocks() const { return m_shaderStorageBlocks; } + + QHash activeUniformsForUniformBlock(int blockIndex) const; + + ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId); + ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex); + ShaderUniformBlock uniformBlockForBlockName(const QString &blockName); + + ShaderStorageBlock storageBlockForBlockIndex(int blockIndex); + ShaderStorageBlock storageBlockForBlockNameId(int blockNameId); + ShaderStorageBlock storageBlockForBlockName(const QString &blockName); + + QOpenGLShaderProgram *shaderProgram() { return &m_shader; } + + void setShaderCode(const QVector shaderCode) { m_shaderCode = shaderCode; } + QVector shaderCode() const; + +private: + bool m_isLoaded; + QOpenGLShaderProgram m_shader; + GraphicsContext *m_graphicsContext; + + QVector m_uniformsNames; + QVector m_uniformsNamesIds; + QVector m_standardUniformNamesIds; + QVector m_uniforms; + + QVector m_attributesNames; + QVector m_attributeNamesIds; + QVector m_attributes; + + QVector m_uniformBlockNames; + QVector m_uniformBlockNamesIds; + QVector m_uniformBlocks; + QHash > m_uniformBlockIndexToShaderUniforms; + + QVector m_shaderStorageBlockNames; + QVector m_shaderStorageBlockNamesIds; + QVector m_shaderStorageBlocks; + + QHash m_fragOutputs; + QVector m_shaderCode; + + // Private so that only GraphicContext can call it + void initializeUniforms(const QVector &uniformsDescription); + void initializeAttributes(const QVector &attributesDescription); + void initializeUniformBlocks(const QVector &uniformBlockDescription); + void initializeShaderStorageBlocks(const QVector &shaderStorageBlockDescription); + + friend class GraphicsContext; + + mutable QMutex m_mutex; + QMetaObject::Connection m_contextConnection; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLSHADER_P_H diff --git a/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp b/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp index 58ce70c28..25a850a79 100644 --- a/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp +++ b/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp @@ -133,7 +133,7 @@ void OpenGLVertexArrayObject::cleanup() } // called from job -bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr) +bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, GLShaderManager *shaderMgr) { QMutexLocker lock(&m_mutex); @@ -141,7 +141,7 @@ bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, ShaderManage return false; const bool geometryExists = (geomMgr->data(m_owners.first) != nullptr); - const bool shaderExists = (shaderMgr->data(m_owners.second) != nullptr); + const bool shaderExists = (shaderMgr->lookupResource(m_owners.second) != nullptr); return !geometryExists || !shaderExists; } diff --git a/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h b/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h index 4896df9bf..362c699da 100644 --- a/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h +++ b/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h @@ -60,9 +60,9 @@ namespace Qt3DRender { namespace Render { class GeometryManager; -class ShaderManager; +class GLShaderManager; -typedef QPair VAOIdentifier; +typedef QPair VAOIdentifier; class OpenGLVertexArrayObject { @@ -77,7 +77,7 @@ public: void destroy(); void cleanup(); - bool isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr); + bool isAbandoned(GeometryManager *geomMgr, GLShaderManager *shaderMgr); QOpenGLVertexArrayObject *vao() { return m_vao.data(); } const QOpenGLVertexArrayObject *vao() const { return m_vao.data(); } diff --git a/src/render/renderers/opengl/renderer/rendercommand.cpp b/src/render/renderers/opengl/renderer/rendercommand.cpp index 072127391..c6d42fde1 100644 --- a/src/render/renderers/opengl/renderer/rendercommand.cpp +++ b/src/render/renderers/opengl/renderer/rendercommand.cpp @@ -45,10 +45,10 @@ namespace Qt3DRender { namespace Render { RenderCommand::RenderCommand() - : m_stateSet(nullptr) + : m_glShader(nullptr) + , m_stateSet(nullptr) , m_depth(0.0f) , m_changeCost(0) - , m_shaderDna(0) , m_type(RenderCommand::Draw) , m_primitiveCount(0) , m_primitiveType(QGeometryRenderer::Triangles) @@ -73,10 +73,10 @@ RenderCommand::RenderCommand() bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept { - return (a.m_vao == b.m_vao && a.m_shader == b.m_shader && a.m_material == b.m_material && + return (a.m_vao == b.m_vao && a.m_glShader == b.m_glShader && a.m_material == b.m_material && a.m_stateSet == b.m_stateSet && a.m_geometry == b.m_geometry && a.m_geometryRenderer == b.m_geometryRenderer && a.m_indirectDrawBuffer == b.m_indirectDrawBuffer && a.m_activeAttributes == b.m_activeAttributes && - a.m_depth == b.m_depth && a.m_changeCost == b.m_changeCost && a.m_shaderDna == b.m_shaderDna && + a.m_depth == b.m_depth && a.m_changeCost == b.m_changeCost && a.m_shaderId == b.m_shaderId && a.m_workGroups[0] == b.m_workGroups[0] && a.m_workGroups[1] == b.m_workGroups[1] && a.m_workGroups[2] == b.m_workGroups[2] && a.m_primitiveCount == b.m_primitiveCount && a.m_primitiveType == b.m_primitiveType && a.m_restartIndexValue == b.m_restartIndexValue && a.m_firstInstance == b.m_firstInstance && a.m_firstVertex == b.m_firstVertex && a.m_verticesPerPatch == b.m_verticesPerPatch && diff --git a/src/render/renderers/opengl/renderer/rendercommand_p.h b/src/render/renderers/opengl/renderer/rendercommand_p.h index e1cf7a4e0..44e34ecfb 100644 --- a/src/render/renderers/opengl/renderer/rendercommand_p.h +++ b/src/render/renderers/opengl/renderer/rendercommand_p.h @@ -72,6 +72,7 @@ namespace Render { class RenderStateSet; using RenderStateSetPtr = QSharedPointer; +class GLShader; class Q_AUTOTEST_EXPORT RenderCommand { @@ -79,8 +80,9 @@ public: RenderCommand(); HVao m_vao; // VAO used during the submission step to store all states and VBOs - HShader m_shader; // Shader for given pass and mesh HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....) + GLShader *m_glShader; // GL Shader to be used at render time + Qt3DCore::QNodeId m_shaderId; // Shader for given pass and mesh ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the // Texture while submission is happening. RenderStateSetPtr m_stateSet; @@ -97,7 +99,6 @@ public: float m_depth; int m_changeCost; - uint m_shaderDna; enum CommandType { Draw, diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index 3957894e5..f76673866 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -241,7 +241,6 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_lastFrameCorrect(0) , m_glContext(nullptr) , m_shareContext(nullptr) - , m_shaderCache(new ShaderCache()) , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create()) , m_rayCastingJob(RayCastingJobPtr::create()) , m_time(0) @@ -328,7 +327,6 @@ Renderer::~Renderer() delete m_renderQueue; delete m_defaultRenderStateSet; - delete m_shaderCache; delete m_glResourceManagers; if (!m_ownedContext) @@ -417,8 +415,9 @@ QOpenGLContext *Renderer::shareContext() const // Executed in the reloadDirtyShader job void Renderer::loadShader(Shader *shader, HShader shaderHandle) { - Q_UNUSED(shader) - m_dirtyShaders.push_back(shaderHandle); + Q_UNUSED(shader); + if (!m_dirtyShaders.contains(shaderHandle)) + m_dirtyShaders.push_back(shaderHandle); } void Renderer::setOpenGLContext(QOpenGLContext *context) @@ -491,9 +490,6 @@ void Renderer::initialize() m_shareContext->create(); } - // Set shader cache on submission context and command thread - m_submissionContext->setShaderCache(m_shaderCache); - // Note: we don't have a surface at this point // The context will be made current later on (at render time) m_submissionContext->setOpenGLContext(ctx); @@ -605,6 +601,10 @@ void Renderer::releaseGraphicsResources() buffer->destroy(m_submissionContext.data()); } + // Do the same thing with shaders + const QVector shaders = m_glResourceManagers->glShaderManager()->takeActiveResources(); + qDeleteAll(shaders); + // Do the same thing with VAOs const QVector activeVaos = m_glResourceManagers->vaoManager()->activeHandles(); for (const HVao &vaoHandle : activeVaos) { @@ -770,6 +770,13 @@ void Renderer::doRender(bool swapBuffers) // 2) Update VAO and copy data into commands to allow concurrent submission prepareCommandsSubmission(renderViews); preprocessingComplete = true; + + // Purge shader which aren't used any longer + static int callCount = 0; + ++callCount; + const int shaderPurgePeriod = 600; + if (callCount % shaderPurgePeriod == 0) + m_glResourceManagers->glShaderManager()->purge(); } } } @@ -923,7 +930,7 @@ void Renderer::prepareCommandsSubmission(const QVector &renderView if (command.m_type == RenderCommand::Draw) { Geometry *rGeometry = m_nodesManager->data(command.m_geometry); GeometryRenderer *rGeometryRenderer = m_nodesManager->data(command.m_geometryRenderer); - Shader *shader = m_nodesManager->data(command.m_shader); + GLShader *shader = command.m_glShader; // We should never have inserted a command for which these are null // in the first place @@ -958,7 +965,7 @@ void Renderer::prepareCommandsSubmission(const QVector &renderView if (!command.m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { Profiling::GLTimeRecorder recorder(Profiling::VAOUpload); // Activate shader - m_submissionContext->activateShader(shader->dna()); + m_submissionContext->activateShader(shader); // Bind VAO vao->bind(); // Update or set Attributes and Buffers for the given rGeometry and Command @@ -978,7 +985,7 @@ void Renderer::prepareCommandsSubmission(const QVector &renderView shader->prepareUniforms(command.m_parameterPack); } else if (command.m_type == RenderCommand::Compute) { - Shader *shader = m_nodesManager->data(command.m_shader); + GLShader *shader = command.m_glShader; Q_ASSERT(shader); // Prepare the ShaderParameterPack based on the active uniforms of the shader @@ -1012,7 +1019,7 @@ void Renderer::lookForAbandonedVaos() // Make sure to only mark VAOs for deletion that were already created // (ignore those that might be currently under construction in the render thread) - if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_nodesManager->shaderManager())) { + if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_glResourceManagers->glShaderManager())) { m_abandonedVaosMutex.lock(); m_abandonedVaos.push_back(handle); m_abandonedVaosMutex.unlock(); @@ -1128,8 +1135,7 @@ void Renderer::reloadDirtyShaders() } } - // If the shader hasn't been loaded, load it - if (shader != nullptr && !shader->isLoaded()) + if (shader != nullptr && shader->isDirty()) loadShader(shader, shaderHandle); } } @@ -1301,7 +1307,7 @@ void Renderer::updateGLResources() continue; // Compile shader - m_submissionContext->loadShader(shader, shaderManager); + m_submissionContext->loadShader(shader, shaderManager, m_glResourceManagers->glShaderManager()); } } #endif @@ -1433,6 +1439,16 @@ void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId) } } +// Render Thread +void Renderer::cleanupShader(const Shader *shader) +{ + GLShaderManager *glShaderManager = m_glResourceManagers->glShaderManager(); + GLShader *glShader = glShaderManager->lookupResource(shader->peerId()); + + if (glShader != nullptr) + glShaderManager->abandon(glShader, shader); +} + // Called by SubmitRenderView void Renderer::downloadGLBuffers() { @@ -2051,7 +2067,8 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command) { { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); - m_submissionContext->activateShader(command->m_shaderDna); + GLShader *shader = m_glResourceManagers->glShaderManager()->lookupResource(command->m_shaderId); + m_submissionContext->activateShader(shader); } { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); @@ -2077,7 +2094,7 @@ void Renderer::createOrUpdateVAO(RenderCommand *command, HVao *previousVaoHandle, OpenGLVertexArrayObject **vao) { - const VAOIdentifier vaoKey(command->m_geometry, command->m_shader); + const VAOIdentifier vaoKey(command->m_geometry, command->m_shaderId); VAOManager *vaoManager = m_glResourceManagers->vaoManager(); command->m_vao = vaoManager->lookupHandle(vaoKey); @@ -2133,7 +2150,8 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); //// We activate the shader here - if (!m_submissionContext->activateShader(command.m_shaderDna)) { + GLShader *shader = command.m_glShader; + if (!m_submissionContext->activateShader(shader)) { allCommandsIssued = false; continue; } @@ -2195,7 +2213,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) bool Renderer::updateVAOWithAttributes(Geometry *geometry, const RenderCommand *command, - Shader *shader, + GLShader *shader, bool forceUpdate) { m_dirtyAttributes.reserve(m_dirtyAttributes.size() + geometry->attributes().size()); @@ -2300,6 +2318,16 @@ void Renderer::cleanGraphicsResources() m_glResourceManagers->vaoManager()->release(vaoHandle); } } + + // Abandon GL shaders when a Shader node is destroyed Note: We are sure + // that when this gets executed, all scene changes have been received and + // shader nodes updated + const QVector cleanedUpShaderIds = m_nodesManager->shaderManager()->takeShaderIdsToCleanup(); + for (const Qt3DCore::QNodeId shaderCleanedUpId: cleanedUpShaderIds) { + cleanupShader(m_nodesManager->shaderManager()->lookupResource(shaderCleanedUpId)); + // We can really release the texture at this point + m_nodesManager->shaderManager()->releaseResource(shaderCleanedUpId); + } } QList> Renderer::pendingPickingEvents() const diff --git a/src/render/renderers/opengl/renderer/renderer.pri b/src/render/renderers/opengl/renderer/renderer.pri index 849bac702..3e2f9fde9 100644 --- a/src/render/renderers/opengl/renderer/renderer.pri +++ b/src/render/renderers/opengl/renderer/renderer.pri @@ -7,7 +7,8 @@ SOURCES += \ $$PWD/renderqueue.cpp \ $$PWD/renderview.cpp \ $$PWD/renderviewbuilder.cpp \ - $$PWD/shaderparameterpack.cpp + $$PWD/shaderparameterpack.cpp \ + $$PWD/glshader.cpp HEADERS += \ $$PWD/openglvertexarrayobject_p.h \ @@ -18,6 +19,6 @@ HEADERS += \ $$PWD/renderview_p.h \ $$PWD/renderviewbuilder_p.h \ $$PWD/shaderparameterpack_p.h \ - $$PWD/shadervariables_p.h - + $$PWD/shadervariables_p.h \ + $$PWD/glshader_p.h diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h index 96d251712..b423252ca 100644 --- a/src/render/renderers/opengl/renderer/renderer_p.h +++ b/src/render/renderers/opengl/renderer/renderer_p.h @@ -149,8 +149,8 @@ class RenderStateSet; class VSyncFrameAdvanceService; class PickEventFilter; class NodeManagers; -class ShaderCache; class GLResourceManagers; +class GLShader; class UpdateLevelOfDetailJob; typedef QSharedPointer UpdateLevelOfDetailJobPtr; @@ -258,6 +258,7 @@ public: void updateGLResources(); void updateTexture(Texture *texture); void cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId); + void cleanupShader(const Shader *shader); void downloadGLBuffers(); void blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, Qt3DCore::QNodeId outputRenderTargetId, @@ -269,7 +270,7 @@ public: bool executeCommandsSubmission(const RenderView *rv); bool updateVAOWithAttributes(Geometry *geometry, const RenderCommand *command, - Shader *shader, + GLShader *shader, bool forceUpdate); bool requiresVAOAttributeUpdate(Geometry *geometry, @@ -358,7 +359,6 @@ private: QOpenGLContext *m_glContext; QOpenGLContext *m_shareContext; mutable QMutex m_shareContextMutex; - ShaderCache *m_shaderCache; PickBoundingVolumeJobPtr m_pickBoundingVolumeJob; RayCastingJobPtr m_rayCastingJob; diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp index 37d417fb6..d77eb3f07 100644 --- a/src/render/renderers/opengl/renderer/renderview.cpp +++ b/src/render/renderers/opengl/renderer/renderview.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -321,7 +322,7 @@ struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a.m_shaderDna == b.m_shaderDna; + return a.m_glShader == b.m_glShader; } }; @@ -409,9 +410,9 @@ struct SubRangeSorter { static void sortSubRange(CommandIt begin, const CommandIt end) { - // First we sort by shaderDNA + // First we sort by shader std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { - return a.m_shaderDna > b.m_shaderDna; + return a.m_glShader > b.m_glShader; }); } }; @@ -532,7 +533,9 @@ void sortCommandRange(QVector &commands, int begin, const int end void RenderView::sort() { - sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes); + // Compares the bitsetKey of the RenderCommands + // Key[Depth | StateCost | Shader] + sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes); // For RenderCommand with the same shader // We compute the adjacent change cost @@ -544,7 +547,8 @@ void RenderView::sort() int j = i; // Advance while commands share the same shader - while (i < commandSize && m_commands[j].m_shaderDna == m_commands[i].m_shaderDna) + while (i < commandSize && + m_commands[j].m_glShader == m_commands[i].m_glShader) ++i; if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes @@ -626,6 +630,7 @@ void RenderView::addClearBuffers(const ClearBuffers *cb) { EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector &entities, int offset, int count) const { + GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); EntityRenderCommandData commands; commands.reserve(count); @@ -668,7 +673,13 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVectormerge(m_stateSet); command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } - command.m_shader = m_manager->lookupHandle(pass->shaderProgram()); + command.m_shaderId = pass->shaderProgram(); + command.m_glShader = glShaderManager->lookupResource(command.m_shaderId); + + // It takes two frames to have a valid command as we can only + // reference a glShader at frame n if it has been loaded at frame n - 1 + if (!command.m_glShader) + continue; { // Scoped to show extent @@ -754,6 +765,7 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVectorglResourceManagers()->glShaderManager(); commands.reserve(count); @@ -784,7 +796,14 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVectormerge(m_stateSet); command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } - command.m_shader = m_manager->lookupHandle(pass->shaderProgram()); + command.m_shaderId = pass->shaderProgram(); + command.m_glShader = glShaderManager->lookupResource(command.m_shaderId); + + // It takes two frames to have a valid command as we can only + // reference a glShader at frame n if it has been loaded at frame n - 1 + if (!command.m_glShader) + continue; + command.m_computeCommand = computeCommandHandle; command.m_type = RenderCommand::Compute; command.m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); @@ -856,6 +875,7 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, ParameterInfoList globalParameters = passData.parameterInfo; // setShaderAndUniforms can initialize a localData // make sure this is cleared before we leave this function + setShaderAndUniforms(&command, globalParameters, entity, @@ -935,7 +955,7 @@ void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, } void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, - Shader *shader, + GLShader *shader, const ShaderUniformBlock &block, const UniformValue &value) const { @@ -956,7 +976,7 @@ void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, } void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, - Shader *shader, + GLShader *shader, const ShaderStorageBlock &block, const UniformValue &value) const { @@ -974,7 +994,7 @@ void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, } } -void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, Shader *shader, ShaderData *shaderData, const QString &structName) const +void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, GLShader *shader, ShaderData *shaderData, const QString &structName) const { UniformBlockValueBuilder *builder = m_localData.localData(); builder->activeUniformNamesToValue.clear(); @@ -1014,10 +1034,8 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // For each ParameterBinder in the RenderPass -> create a QUniformPack // Once that works, improve that to try and minimize QUniformPack updates - // Index Shader by Shader UUID - Shader *shader = m_manager->data(command->m_shader); + GLShader *shader = command->m_glShader; if (shader != nullptr && shader->isLoaded()) { - command->m_shaderDna = shader->dna(); // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h index c7dc37a2c..5741a88a5 100644 --- a/src/render/renderers/opengl/renderer/renderview_p.h +++ b/src/render/renderers/opengl/renderer/renderview_p.h @@ -386,15 +386,15 @@ private: Entity *entity, const Matrix4x4 &worldTransform) const; void setUniformBlockValue(ShaderParameterPack &uniformPack, - Shader *shader, + GLShader *shader, const ShaderUniformBlock &block, const UniformValue &value) const; void setShaderStorageValue(ShaderParameterPack &uniformPack, - Shader *shader, + GLShader *shader, const ShaderStorageBlock &block, const UniformValue &value) const; void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, - Shader *shader, + GLShader *shader, ShaderData *shaderData, const QString &structName) const; }; diff --git a/tests/auto/render/glshadermanager/glshadermanager.pro b/tests/auto/render/glshadermanager/glshadermanager.pro new file mode 100644 index 000000000..27aadf84f --- /dev/null +++ b/tests/auto/render/glshadermanager/glshadermanager.pro @@ -0,0 +1,12 @@ +TEMPLATE = app + +TARGET = tst_glshadermanager + +QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib + +CONFIG += testcase + +SOURCES += tst_glshadermanager.cpp + +include(../../core/common/common.pri) +include(../commons/commons.pri) diff --git a/tests/auto/render/glshadermanager/tst_glshadermanager.cpp b/tests/auto/render/glshadermanager/tst_glshadermanager.cpp new file mode 100644 index 000000000..c18fb4793 --- /dev/null +++ b/tests/auto/render/glshadermanager/tst_glshadermanager.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include "qbackendnodetester.h" +#include "testrenderer.h" + +class tst_GLShaderManager : public Qt3DCore::QBackendNodeTester +{ + Q_OBJECT + +private Q_SLOTS: + void adopt(); + void lookupResource(); + void abandon(); + void insertAfterRemoval(); +}; + +void tst_GLShaderManager::adopt() +{ + // GIVEN + Qt3DRender::Render::GLShaderManager cache; + Qt3DRender::QShaderProgram frontendShader1; + Qt3DRender::QShaderProgram frontendShader2; + TestRenderer renderer; + Qt3DRender::Render::Shader backendShaderNode1; + Qt3DRender::Render::Shader backendShaderNode2; + + backendShaderNode1.setRenderer(&renderer); + backendShaderNode2.setRenderer(&renderer); + simulateInitialization(&frontendShader1, &backendShaderNode1); + simulateInitialization(&frontendShader2, &backendShaderNode2); + + // THEN + QVERIFY(cache.lookupResource(backendShaderNode1.peerId()) == nullptr); + QVERIFY(cache.lookupResource(backendShaderNode2.peerId()) == nullptr); + QVERIFY(backendShaderNode1.peerId() != backendShaderNode2.peerId()); + + // WHEN + Qt3DRender::Render::GLShader *glShader1 = cache.createOrAdoptExisting(&backendShaderNode1); + + // THEN + QVERIFY(glShader1 != nullptr); + QVector shaderNodeIds = cache.shaderIdsForProgram(glShader1); + QCOMPARE(shaderNodeIds.size(), 1); + QCOMPARE(shaderNodeIds.first(), backendShaderNode1.peerId()); + + // WHEN + Qt3DRender::Render::GLShader *glShader2 = cache.createOrAdoptExisting(&backendShaderNode2); + + // THEN + QCOMPARE(glShader1, glShader2); + + shaderNodeIds = cache.shaderIdsForProgram(glShader2); + QCOMPARE(shaderNodeIds.size(), 2); + QCOMPARE(shaderNodeIds.first(), backendShaderNode1.peerId()); + QCOMPARE(shaderNodeIds.last(), backendShaderNode2.peerId()); +} + +void tst_GLShaderManager::lookupResource() +{ + // GIVEN + Qt3DRender::Render::GLShaderManager cache; + Qt3DRender::QShaderProgram frontendShader1; + Qt3DRender::QShaderProgram frontendShader2; + TestRenderer renderer; + Qt3DRender::Render::Shader backendShaderNode1; + Qt3DRender::Render::Shader backendShaderNode2; + + backendShaderNode1.setRenderer(&renderer); + backendShaderNode2.setRenderer(&renderer); + simulateInitialization(&frontendShader1, &backendShaderNode1); + simulateInitialization(&frontendShader2, &backendShaderNode2); + + // WHEN + cache.createOrAdoptExisting(&backendShaderNode1); + cache.createOrAdoptExisting(&backendShaderNode2); + + // THEN + Qt3DRender::Render::GLShader *glShader1 = cache.lookupResource(backendShaderNode1.peerId()); + Qt3DRender::Render::GLShader *glShader2 = cache.lookupResource(backendShaderNode2.peerId()); + QVERIFY(glShader1 != nullptr); + QCOMPARE(glShader1, glShader2); + const QVector shaderNodeIds = cache.shaderIdsForProgram(glShader1); + QCOMPARE(shaderNodeIds.size(), 2); + QVERIFY(shaderNodeIds.contains(frontendShader1.id())); + QVERIFY(shaderNodeIds.contains(frontendShader2.id())); +} + +void tst_GLShaderManager::abandon() +{ + // GIVEN + Qt3DRender::Render::GLShaderManager cache; + Qt3DRender::QShaderProgram frontendShader1; + Qt3DRender::QShaderProgram frontendShader2; + TestRenderer renderer; + Qt3DRender::Render::Shader backendShaderNode1; + Qt3DRender::Render::Shader backendShaderNode2; + + backendShaderNode1.setRenderer(&renderer); + backendShaderNode2.setRenderer(&renderer); + simulateInitialization(&frontendShader1, &backendShaderNode1); + simulateInitialization(&frontendShader2, &backendShaderNode2); + cache.createOrAdoptExisting(&backendShaderNode1); + cache.createOrAdoptExisting(&backendShaderNode2); + + // WHEN + Qt3DRender::Render::GLShader *glShader = cache.lookupResource(backendShaderNode1.peerId()); + cache.abandon(glShader, &backendShaderNode1); + + // THEN + QVector shaderNodeIds = cache.shaderIdsForProgram(glShader); + QVERIFY(cache.takeAbandonned().isEmpty()); + QCOMPARE(shaderNodeIds.size(), 1); + QCOMPARE(shaderNodeIds.first(), backendShaderNode2.peerId()); + + // WHEN + cache.abandon(glShader, &backendShaderNode2); + + // THEN + shaderNodeIds = cache.shaderIdsForProgram(glShader); + QCOMPARE(shaderNodeIds.size(), 0); + const QVector releasedShaders = cache.takeAbandonned(); + QCOMPARE(releasedShaders.size(), 1); + QCOMPARE(releasedShaders.first(), glShader); +} + +void tst_GLShaderManager::insertAfterRemoval() +{ + // GIVEN + Qt3DRender::Render::GLShaderManager cache; + Qt3DRender::QShaderProgram frontendShader; + TestRenderer renderer; + Qt3DRender::Render::Shader backendShaderNode; + + + backendShaderNode.setRenderer(&renderer); + simulateInitialization(&frontendShader, &backendShaderNode); + + // WHEN + Qt3DRender::Render::GLShader *apiShader1 = cache.createOrAdoptExisting(&backendShaderNode); + const Qt3DRender::Render::GLShader *originalApiShader = apiShader1; + + // THEN + auto apiShader2 = cache.lookupResource(frontendShader.id()); + QVERIFY(apiShader1 != nullptr); + QVERIFY(apiShader2 != nullptr); + QVERIFY(apiShader1 == originalApiShader); + QVERIFY(apiShader1 == apiShader2); + + // WHEN + cache.abandon(apiShader1, &backendShaderNode); + + // THEN + Qt3DRender::Render::GLShader *apiShaderEmpty = cache.lookupResource(frontendShader.id()); + QVERIFY(apiShaderEmpty == nullptr); + + // WHEN + apiShader1 = cache.createOrAdoptExisting(&backendShaderNode); + cache.purge(); + apiShader2 = cache.lookupResource(frontendShader.id()); + + // THEN + QVERIFY(apiShader1 != nullptr); + QVERIFY(apiShader2 != nullptr); + QVERIFY(apiShader1 == apiShader2); + QVERIFY(apiShader2 == originalApiShader); +} + +QTEST_APPLESS_MAIN(tst_GLShaderManager) + +#include "tst_glshadermanager.moc" diff --git a/tests/auto/render/render.pro b/tests/auto/render/render.pro index 908426570..2b73ec8b7 100644 --- a/tests/auto/render/render.pro +++ b/tests/auto/render/render.pro @@ -42,7 +42,6 @@ qtConfig(private_tests) { # boundingvolumedebug \ ddstextures \ ktxtextures \ - shadercache \ layerfiltering \ filterentitybycomponent \ genericlambdajob \ @@ -130,6 +129,7 @@ qtConfig(qt3d-opengl-renderer):qtConfig(private_tests) { graphicshelpergl3_3 \ graphicshelpergl3_2 \ graphicshelpergl2 \ + glshadermanager \ materialparametergathererjob \ textures \ renderer \ diff --git a/tests/auto/render/renderviews/tst_renderviews.cpp b/tests/auto/render/renderviews/tst_renderviews.cpp index 1558b68c9..dca75e418 100644 --- a/tests/auto/render/renderviews/tst_renderviews.cpp +++ b/tests/auto/render/renderviews/tst_renderviews.cpp @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include QT_BEGIN_NAMESPACE @@ -64,6 +67,7 @@ void compareShaderParameterPacks(const ShaderParameterPack &t1, class tst_RenderViews : public Qt3DCore::QBackendNodeTester { Q_OBJECT + private Q_SLOTS: void checkRenderViewSizeFitsWithAllocator() @@ -132,10 +136,15 @@ private Q_SLOTS: void checkRenderCommandBackToFrontSorting() { // GIVEN + Qt3DRender::Render::NodeManagers nodeManagers; + Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); RenderView renderView; QVector rawCommands; QVector sortTypes; + renderer.setNodeManagers(&nodeManagers); + renderView.setRenderer(&renderer); + sortTypes.push_back(QSortPolicy::BackToFront); for (int i = 0; i < 200; ++i) { @@ -156,28 +165,34 @@ private Q_SLOTS: QVERIFY(sortedCommands.at(j - 1).m_depth > sortedCommands.at(j).m_depth); // RenderCommands are deleted by RenderView dtor + renderer.shutdown(); } void checkRenderCommandMaterialSorting() { // GIVEN + Qt3DRender::Render::NodeManagers nodeManagers; + Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); RenderView renderView; QVector rawCommands; QVector sortTypes; + renderer.setNodeManagers(&nodeManagers); + renderView.setRenderer(&renderer); + sortTypes.push_back(QSortPolicy::Material); - ProgramDNA dnas[5] = { - ProgramDNA(250), - ProgramDNA(500), - ProgramDNA(1000), - ProgramDNA(1500), - ProgramDNA(2000), + GLShader *dnas[5] = { + reinterpret_cast(0x250), + reinterpret_cast(0x500), + reinterpret_cast(0x1000), + reinterpret_cast(0x1500), + reinterpret_cast(0x2000) }; for (int i = 0; i < 20; ++i) { RenderCommand c; - c.m_shaderDna = dnas[i % 5]; + c.m_glShader = dnas[i % 5]; rawCommands.push_back(c); } @@ -189,24 +204,25 @@ private Q_SLOTS: // THEN const QVector sortedCommands = renderView.commands(); QCOMPARE(rawCommands.size(), sortedCommands.size()); - ProgramDNA targetDNA; + GLShader *targetShader; for (int j = 0; j < sortedCommands.size(); ++j) { if (j % 4 == 0) { - targetDNA = sortedCommands.at(j).m_shaderDna; + targetShader = sortedCommands.at(j).m_glShader; if (j > 0) - QVERIFY(targetDNA != sortedCommands.at(j - 1).m_shaderDna); + QVERIFY(targetShader != sortedCommands.at(j - 1).m_glShader); } - QCOMPARE(targetDNA, sortedCommands.at(j).m_shaderDna); + QCOMPARE(targetShader, sortedCommands.at(j).m_glShader); } // RenderCommands are deleted by RenderView dtor + renderer.shutdown(); } void checkRenderViewUniformMinification_data() { - QTest::addColumn>("programDNAs"); + QTest::addColumn>("shaders"); QTest::addColumn>("rawParameters"); QTest::addColumn>("expectedMinimizedParameters"); @@ -217,36 +233,56 @@ private Q_SLOTS: pack1.setUniform(2, UniformValue(1584.0f)); pack1.setTexture(3, 0, fakeTextureNodeId); + QShaderProgram *shader1 = new QShaderProgram(); + QShaderProgram *shader2 = new QShaderProgram(); + + shader1->setShaderCode(QShaderProgram::Vertex, QByteArrayLiteral("1")); + shader2->setShaderCode(QShaderProgram::Vertex, QByteArrayLiteral("2")); + ShaderParameterPack minifiedPack1; QTest::newRow("NoMinification") - << (QVector() << ProgramDNA(883) << ProgramDNA(1584)) + << (QVector() << shader1 << shader2) << (QVector() << pack1 << pack1) << (QVector() << pack1 << pack1); QTest::newRow("SingleShaderMinified") - << (QVector() << ProgramDNA(883) << ProgramDNA(883) << ProgramDNA(883)) + << (QVector() << shader1 << shader1 << shader1) << (QVector() << pack1 << pack1 << pack1) << (QVector() << pack1 << minifiedPack1 << minifiedPack1); QTest::newRow("MultipleShadersMinified") - << (QVector() << ProgramDNA(883) << ProgramDNA(883) << ProgramDNA(883) << ProgramDNA(1584) << ProgramDNA(1584) << ProgramDNA(1584)) + << (QVector() << shader1 << shader1 << shader1 << shader2 << shader2 << shader2) << (QVector() << pack1 << pack1 << pack1 << pack1 << pack1 << pack1) << (QVector() << pack1 << minifiedPack1 << minifiedPack1 << pack1 << minifiedPack1 << minifiedPack1); } void checkRenderViewUniformMinification() { - QFETCH(QVector, programDNAs); + QFETCH(QVector, shaders); QFETCH(QVector, rawParameters); QFETCH(QVector, expectedMinimizedParameters); + Qt3DRender::Render::NodeManagers nodeManagers; + Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); + renderer.setNodeManagers(&nodeManagers); + + GLShaderManager *shaderManager = renderer.glResourceManagers()->glShaderManager(); + for (int i = 0, m = shaders.size(); i < m; ++i) { + Shader* backend = new Shader(); + backend->setRenderer(&renderer); + simulateInitializationSync(shaders.at(i), backend); + shaderManager->createOrAdoptExisting(backend); + } + RenderView renderView; QVector rawCommands; + renderView.setRenderer(&renderer); - for (int i = 0, m = programDNAs.size(); i < m; ++i) { + for (int i = 0, m = shaders.size(); i < m; ++i) { RenderCommand c; - c.m_shaderDna = programDNAs.at(i); + c.m_shaderId = shaders.at(i)->id(); + c.m_glShader = shaderManager->lookupResource(c.m_shaderId); c.m_parameterPack = rawParameters.at(i); rawCommands.push_back(c); } @@ -259,21 +295,28 @@ private Q_SLOTS: const QVector sortedCommands = renderView.commands(); QCOMPARE(rawCommands, sortedCommands); - for (int i = 0, m = programDNAs.size(); i < m; ++i) { + for (int i = 0, m = shaders.size(); i < m; ++i) { const RenderCommand c = sortedCommands.at(i); - QCOMPARE(c.m_shaderDna, programDNAs.at(i)); + QCOMPARE(c.m_shaderId, shaders.at(i)->id()); compareShaderParameterPacks(c.m_parameterPack, expectedMinimizedParameters.at(i)); } + + renderer.shutdown(); } void checkRenderCommandFrontToBackSorting() { // GIVEN + Qt3DRender::Render::NodeManagers nodeManagers; + Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); RenderView renderView; QVector rawCommands; QVector sortTypes; + renderer.setNodeManagers(&nodeManagers); + renderView.setRenderer(&renderer); + sortTypes.push_back(QSortPolicy::FrontToBack); for (int i = 0; i < 200; ++i) { @@ -294,15 +337,21 @@ private Q_SLOTS: QVERIFY(sortedCommands.at(j - 1).m_depth < sortedCommands.at(j).m_depth); // RenderCommands are deleted by RenderView dtor + renderer.shutdown(); } void checkRenderCommandStateCostSorting() { // GIVEN + Qt3DRender::Render::NodeManagers nodeManagers; + Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); RenderView renderView; QVector rawCommands; QVector sortTypes; + renderer.setNodeManagers(&nodeManagers); + renderView.setRenderer(&renderer); + sortTypes.push_back(QSortPolicy::StateChangeCost); for (int i = 0; i < 200; ++i) { @@ -323,24 +372,31 @@ private Q_SLOTS: QVERIFY(sortedCommands.at(j - 1).m_changeCost > sortedCommands.at(j).m_changeCost); // RenderCommands are deleted by RenderView dtor + renderer.shutdown(); } void checkRenderCommandCombinedStateMaterialDepthSorting() { // GIVEN + Qt3DRender::Render::NodeManagers nodeManagers; + Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); RenderView renderView; QVector rawCommands; QVector sortTypes; + renderer.setNodeManagers(&nodeManagers); + renderView.setRenderer(&renderer); + sortTypes.push_back(QSortPolicy::StateChangeCost); sortTypes.push_back(QSortPolicy::Material); sortTypes.push_back(QSortPolicy::BackToFront); - ProgramDNA dna[4] = { - ProgramDNA(250), - ProgramDNA(500), - ProgramDNA(1000), - ProgramDNA(1500) + GLShader *dna[5] = { + reinterpret_cast(0x250), + reinterpret_cast(0x500), + reinterpret_cast(0x1000), + reinterpret_cast(0x1500), + reinterpret_cast(0x2000) }; float depth[3] = { @@ -354,9 +410,9 @@ private Q_SLOTS: 200 }; - auto buildRC = [] (ProgramDNA dna, float depth, int changeCost) { + auto buildRC = [] (GLShader *dna, float depth, int changeCost) { RenderCommand c; - c.m_shaderDna = dna; + c.m_glShader = dna; c.m_depth = depth; c.m_changeCost = changeCost; return c; @@ -399,6 +455,7 @@ private Q_SLOTS: QCOMPARE(c9, sortedCommands.at(6)); // RenderCommands are deleted by RenderView dtor + renderer.shutdown(); } void checkRenderCommandTextureSorting() diff --git a/tests/auto/render/shader/tst_shader.cpp b/tests/auto/render/shader/tst_shader.cpp index d1578aee7..3f0714907 100644 --- a/tests/auto/render/shader/tst_shader.cpp +++ b/tests/auto/render/shader/tst_shader.cpp @@ -79,15 +79,9 @@ void tst_RenderShader::hasCoherentInitialState() { Qt3DRender::Render::Shader *shader = new Qt3DRender::Render::Shader(); - QCOMPARE(shader->isLoaded(), false); - QCOMPARE(shader->dna(), 0U); - QVERIFY(shader->uniformsNames().isEmpty()); - QVERIFY(shader->attributesNames().isEmpty()); - QVERIFY(shader->uniformBlockNames().isEmpty()); - QVERIFY(shader->uniforms().isEmpty()); - QVERIFY(shader->attributes().isEmpty()); - QVERIFY(shader->uniformBlocks().isEmpty()); QCOMPARE(shader->status(), Qt3DRender::QShaderProgram::NotReady); + QVERIFY(shader->log().isEmpty()); + QCOMPARE(shader->isDirty(), false); } void tst_RenderShader::matchesFrontendPeer() @@ -98,8 +92,7 @@ void tst_RenderShader::matchesFrontendPeer() backend.setRenderer(&renderer); simulateInitializationSync(frontend.data(), &backend); - QCOMPARE(backend.isLoaded(), false); - QVERIFY(backend.dna() != 0U); + QCOMPARE(backend.isDirty(), true); for (int i = Qt3DRender::QShaderProgram::Vertex; i <= Qt3DRender::QShaderProgram::Compute; ++i) QCOMPARE(backend.shaderCode()[i], @@ -117,14 +110,7 @@ void tst_RenderShader::cleanupLeavesACoherentState() shader.cleanup(); - QCOMPARE(shader.isLoaded(), false); - QCOMPARE(shader.dna(), 0U); - QVERIFY(shader.uniformsNames().isEmpty()); - QVERIFY(shader.attributesNames().isEmpty()); - QVERIFY(shader.uniformBlockNames().isEmpty()); - QVERIFY(shader.uniforms().isEmpty()); - QVERIFY(shader.attributes().isEmpty()); - QVERIFY(shader.uniformBlocks().isEmpty()); + QCOMPARE(shader.isDirty(), false); QCOMPARE(shader.status(), Qt3DRender::QShaderProgram::NotReady); } @@ -152,7 +138,7 @@ void tst_RenderShader::dealWithPropertyChanges() Qt3DRender::Render::Shader backend; Qt3DRender::QShaderProgram shader; - backend.setLoaded(true); + TestRenderer renderer; backend.setRenderer(&renderer); simulateInitializationSync(&shader, &backend); @@ -162,11 +148,13 @@ void tst_RenderShader::dealWithPropertyChanges() backend.syncFromFrontEnd(&shader, false); // THEN - QCOMPARE(backend.shaderCode().at(type), QByteArrayLiteral("foo")); - QVERIFY(!backend.isLoaded()); + QCOMPARE(backend.shaderCode().at(type), QStringLiteral("foo")); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); + QCOMPARE(backend.isDirty(), true); + renderer.resetDirty(); - backend.setLoaded(true); + QCOMPARE(renderer.dirtyBits(), 0); + backend.unsetDirty(); // WHEN shader.setShaderCode(type, QByteArrayLiteral("foo")); @@ -174,10 +162,8 @@ void tst_RenderShader::dealWithPropertyChanges() // THEN QCOMPARE(backend.shaderCode().at(type), QByteArrayLiteral("foo")); - QVERIFY(backend.isLoaded()); QCOMPARE(renderer.dirtyBits(), 0); - renderer.resetDirty(); - backend.setLoaded(true); + QCOMPARE(backend.isDirty(), false); // WHEN shader.setShaderCode(type, QByteArrayLiteral("bar")); @@ -185,10 +171,9 @@ void tst_RenderShader::dealWithPropertyChanges() // THEN QCOMPARE(backend.shaderCode().at(type), QByteArrayLiteral("bar")); - QVERIFY(!backend.isLoaded()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); - backend.setLoaded(true); + QCOMPARE(backend.isDirty(), true); } void tst_RenderShader::checkSetRendererDirtyOnInitialization() @@ -221,7 +206,6 @@ void tst_RenderShader::allowToChangeShaderCode() QFETCH(Qt3DRender::QShaderProgram::ShaderType, type); Qt3DRender::Render::Shader backend; - backend.setLoaded(true); TestRenderer renderer; backend.setRenderer(&renderer); @@ -230,30 +214,24 @@ void tst_RenderShader::allowToChangeShaderCode() // THEN QCOMPARE(backend.shaderCode().at(type), QStringLiteral("foo")); - QVERIFY(!backend.isLoaded()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); - backend.setLoaded(true); // WHEN backend.setShaderCode(type, QByteArrayLiteral("foo")); // THEN QCOMPARE(backend.shaderCode().at(type), QStringLiteral("foo")); - QVERIFY(backend.isLoaded()); QCOMPARE(renderer.dirtyBits(), 0); renderer.resetDirty(); - backend.setLoaded(true); // WHEN backend.setShaderCode(type, QByteArrayLiteral("bar")); // THEN QCOMPARE(backend.shaderCode().at(type), QStringLiteral("bar")); - QVERIFY(!backend.isLoaded()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); - backend.setLoaded(true); } QTEST_APPLESS_MAIN(tst_RenderShader) diff --git a/tests/auto/render/shadercache/shadercache.pro b/tests/auto/render/shadercache/shadercache.pro deleted file mode 100644 index 38499588d..000000000 --- a/tests/auto/render/shadercache/shadercache.pro +++ /dev/null @@ -1,11 +0,0 @@ -TEMPLATE = app - -TARGET = tst_shadercache - -QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib - -CONFIG += testcase - -SOURCES += tst_shadercache.cpp - -include(../../core/common/common.pri) diff --git a/tests/auto/render/shadercache/tst_shadercache.cpp b/tests/auto/render/shadercache/tst_shadercache.cpp deleted file mode 100644 index 261548d69..000000000 --- a/tests/auto/render/shadercache/tst_shadercache.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt3D module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace Qt3DCore; - -namespace Qt3DRender { -namespace Render { - -class tst_ShaderCache : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - void insert(); - void insertAfterRemoval(); - void value(); - void removeRef(); - void purge(); - void destruction(); -}; - -void tst_ShaderCache::insert() -{ - // GIVEN - ShaderCache cache; - - // THEN - QCOMPARE(cache.m_programHash.isEmpty(), true); - QCOMPARE(cache.m_programRefs.isEmpty(), true); - QCOMPARE(cache.m_pendingRemoval.isEmpty(), true); - - // WHEN - auto dna = ProgramDNA(12345); - auto nodeId = QNodeId::createId(); - auto shaderProgram = new QOpenGLShaderProgram; - cache.insert(dna, nodeId, shaderProgram); - - // THEN - QCOMPARE(cache.m_programHash.size(), 1); - QCOMPARE(cache.m_programHash.keys().first(), dna); - QCOMPARE(cache.m_programHash.values().first(), shaderProgram); - - QCOMPARE(cache.m_programRefs.size(), 1); - QCOMPARE(cache.m_programRefs.keys().first(), dna); - QCOMPARE(cache.m_programRefs.values().first().size(), 1); - QCOMPARE(cache.m_programRefs.values().first().first(), nodeId); - - QCOMPARE(cache.m_pendingRemoval.isEmpty(), true); -} - -void tst_ShaderCache::insertAfterRemoval() -{ - // GIVEN - ShaderCache cache; - auto dna = ProgramDNA(12345); - auto nodeId = QNodeId::createId(); - - // WHEN - QOpenGLShaderProgram *shaderProgram = new QOpenGLShaderProgram(); - cache.insert(dna, nodeId, shaderProgram); - cache.getShaderProgramAndAddRef(dna, nodeId); - cache.removeRef(dna, nodeId); - shaderProgram = cache.getShaderProgramAndAddRef(dna, nodeId); - - // THEN - QVERIFY(!cache.m_pendingRemoval.contains(dna)); - - // WHEN - cache.removeRef(dna, nodeId); - cache.getShaderProgramAndAddRef(dna, nodeId); - cache.purge(); - - // THEN - QCOMPARE(cache.m_programHash.size(), 1); -} - -void tst_ShaderCache::value() -{ - // GIVEN - ShaderCache cache; - - // WHEN - auto dnaA = ProgramDNA(12345); - auto nodeIdA = QNodeId::createId(); - auto shaderProgramA = new QOpenGLShaderProgram; - cache.insert(dnaA, nodeIdA, shaderProgramA); - auto cachedProgramA = cache.getShaderProgramAndAddRef(dnaA, nodeIdA); - - // THEN - QCOMPARE(shaderProgramA, cachedProgramA); - - // WHEN - auto nodeIdA2 = QNodeId::createId(); - auto cachedProgramA2 = cache.getShaderProgramAndAddRef(dnaA, nodeIdA2); - - // THEN - QCOMPARE(shaderProgramA, cachedProgramA2); - QCOMPARE(cache.m_programHash.size(), 1); - QCOMPARE(cache.m_programHash.keys().first(), dnaA); - QCOMPARE(cache.m_programHash.values().first(), shaderProgramA); - - QCOMPARE(cache.m_programRefs.size(), 1); - QCOMPARE(cache.m_programRefs.keys().first(), dnaA); - const QVector refsA = cache.m_programRefs.values().first(); - QCOMPARE(refsA.size(), 2); - QCOMPARE(refsA.at(0), nodeIdA); - QCOMPARE(refsA.at(1), nodeIdA2); - - // WHEN - auto dnaB = ProgramDNA(67890); - auto nodeIdB = QNodeId::createId(); - auto shaderProgramB = new QOpenGLShaderProgram; - cache.insert(dnaB, nodeIdB, shaderProgramB); - - // THEN - QCOMPARE(cache.m_programHash.size(), 2); - QCOMPARE(cache.m_programRefs.size(), 2); - - // WHEN - auto cachedProgramB = cache.getShaderProgramAndAddRef(dnaB, nodeIdB); - QCOMPARE(shaderProgramB, cachedProgramB); - - // WHEN - auto dnaC = ProgramDNA(54321); - auto uncachedProgram = cache.getShaderProgramAndAddRef(dnaC, nodeIdB); - QVERIFY(uncachedProgram == nullptr); - - cache.clear(); - // Test inserting nullptr. - cache.insert(dnaA, nodeIdA, nullptr); - bool wasPresent = false; - cachedProgramA = cache.getShaderProgramAndAddRef(dnaA, nodeIdA, &wasPresent); - QCOMPARE(wasPresent, true); - QCOMPARE(cachedProgramA, nullptr); - cache.clear(); - // Test wasPresent==false. - cachedProgramB = cache.getShaderProgramAndAddRef(dnaB, nodeIdB, &wasPresent); - QCOMPARE(wasPresent, false); - QCOMPARE(cachedProgramB, nullptr); -} - -void tst_ShaderCache::removeRef() -{ - // GIVEN - ShaderCache cache; - - // WHEN we add 2 references and remove one - auto dnaA = ProgramDNA(12345); - auto nodeIdA = QNodeId::createId(); - auto shaderProgramA = new QOpenGLShaderProgram; - cache.insert(dnaA, nodeIdA, shaderProgramA); - auto cachedProgramA = cache.getShaderProgramAndAddRef(dnaA, nodeIdA); - - auto nodeIdA2 = QNodeId::createId(); - auto cachedProgramA2 = cache.getShaderProgramAndAddRef(dnaA, nodeIdA2); - - cache.removeRef(dnaA, nodeIdA); - - // THEN - QCOMPARE(cachedProgramA, shaderProgramA); - QCOMPARE(cachedProgramA2, shaderProgramA); - QCOMPARE(cache.m_programHash.size(), 1); - QCOMPARE(cache.m_programRefs.size(), 1); - const auto refs = cache.m_programRefs.value(dnaA); - QCOMPARE(refs.size(), 1); - QCOMPARE(refs.first(), nodeIdA2); - QCOMPARE(cache.m_pendingRemoval.size(), 0); - - // WHEN we remove same ref again - cache.removeRef(dnaA, nodeIdA); - - // THEN no change - QCOMPARE(cache.m_programHash.size(), 1); - QCOMPARE(cache.m_programRefs.size(), 1); - const auto refs2 = cache.m_programRefs.value(dnaA); - QCOMPARE(refs2.size(), 1); - QCOMPARE(refs.first(), nodeIdA2); - - // WHEN we remove other reference - cache.removeRef(dnaA, nodeIdA2); - - // THEN - QCOMPARE(cache.m_programHash.size(), 1); - QCOMPARE(cache.m_programRefs.size(), 1); - const auto refs3 = cache.m_programRefs.value(dnaA); - QCOMPARE(refs3.size(), 0); - QCOMPARE(cache.m_pendingRemoval.size(), 1); - QCOMPARE(cache.m_pendingRemoval.first(), dnaA); -} - -void tst_ShaderCache::purge() -{ - // GIVEN - ShaderCache cache; - - // WHEN we add 2 references and remove one and purge - auto dnaA = ProgramDNA(12345); - auto nodeIdA = QNodeId::createId(); - auto shaderProgramA = new QOpenGLShaderProgram; - QPointer progPointer(shaderProgramA); - cache.insert(dnaA, nodeIdA, shaderProgramA); - auto cachedProgramA = cache.getShaderProgramAndAddRef(dnaA, nodeIdA); - - auto nodeIdA2 = QNodeId::createId(); - auto cachedProgramA2 = cache.getShaderProgramAndAddRef(dnaA, nodeIdA2); - - cache.removeRef(dnaA, nodeIdA); - cache.purge(); - - // THEN no removal - QCOMPARE(cachedProgramA, shaderProgramA); - QCOMPARE(cachedProgramA2, shaderProgramA); - QCOMPARE(cache.m_programHash.size(), 1); - QCOMPARE(cache.m_programRefs.size(), 1); - QCOMPARE(cache.m_pendingRemoval.isEmpty(), true); - - // WHEN we remove final ref and purge - cache.removeRef(dnaA, nodeIdA2); - cache.purge(); - - // THEN shader program is removed from cache and deleted - QCOMPARE(cache.m_programHash.isEmpty(), true); - QCOMPARE(cache.m_programRefs.isEmpty(), true); - QCOMPARE(progPointer.isNull(), true); -} - -void tst_ShaderCache::destruction() -{ - // GIVEN - auto cache = new ShaderCache; - - // WHEN - auto dnaA = ProgramDNA(12345); - auto nodeIdA = QNodeId::createId(); - auto shaderProgramA = new QOpenGLShaderProgram; - QPointer progPointerA(shaderProgramA); - - auto dnaB = ProgramDNA(67890); - auto nodeIdB = QNodeId::createId(); - auto shaderProgramB = new QOpenGLShaderProgram; - QPointer progPointerB(shaderProgramB); - - cache->insert(dnaA, nodeIdA, shaderProgramA); - cache->insert(dnaB, nodeIdB, shaderProgramB); - delete cache; - - // THEN - QCOMPARE(progPointerA.isNull(), true); - QCOMPARE(progPointerB.isNull(), true); -} - -} // namespace Render -} // namespace Qt3DRender - -QT_END_NAMESPACE - -QTEST_APPLESS_MAIN(Qt3DRender::Render::tst_ShaderCache) - -#include "tst_shadercache.moc" -- cgit v1.2.3