summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2017-06-07 10:49:04 +0200
committerPaul Lemire <paul.lemire@kdab.com>2019-12-18 10:58:52 +0100
commitcd8898d92cdd483c7a34f30236b4537983e1b608 (patch)
tree40b8daf57ff3085e7afbd2ff59093d8e66b685ce
parent24dd795de773c00d5cfe64e887ce63719db88015 (diff)
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
-rw-r--r--src/render/backend/apishadermanager_p.h224
-rw-r--r--src/render/backend/commandexecuter.cpp2
-rw-r--r--src/render/backend/managers_p.h15
-rw-r--r--src/render/backend/render-backend.pri3
-rw-r--r--src/render/frontend/qrenderaspect.cpp2
-rw-r--r--src/render/materialsystem/materialsystem.pri2
-rw-r--r--src/render/materialsystem/shader.cpp365
-rw-r--r--src/render/materialsystem/shader_p.h97
-rw-r--r--src/render/materialsystem/shadercache.cpp174
-rw-r--r--src/render/materialsystem/shaderimage.cpp1
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp96
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h20
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp24
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h4
-rw-r--r--src/render/renderers/opengl/managers/glresourcemanagers.cpp3
-rw-r--r--src/render/renderers/opengl/managers/glresourcemanagers_p.h12
-rw-r--r--src/render/renderers/opengl/managers/managers.pri1
-rw-r--r--src/render/renderers/opengl/renderer/glshader.cpp316
-rw-r--r--src/render/renderers/opengl/renderer/glshader_p.h160
-rw-r--r--src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp4
-rw-r--r--src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h6
-rw-r--r--src/render/renderers/opengl/renderer/rendercommand.cpp8
-rw-r--r--src/render/renderers/opengl/renderer/rendercommand_p.h5
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp64
-rw-r--r--src/render/renderers/opengl/renderer/renderer.pri7
-rw-r--r--src/render/renderers/opengl/renderer/renderer_p.h6
-rw-r--r--src/render/renderers/opengl/renderer/renderview.cpp44
-rw-r--r--src/render/renderers/opengl/renderer/renderview_p.h6
-rw-r--r--tests/auto/render/glshadermanager/glshadermanager.pro (renamed from tests/auto/render/shadercache/shadercache.pro)5
-rw-r--r--tests/auto/render/glshadermanager/tst_glshadermanager.cpp199
-rw-r--r--tests/auto/render/render.pro2
-rw-r--r--tests/auto/render/renderviews/tst_renderviews.cpp111
-rw-r--r--tests/auto/render/shader/tst_shader.cpp46
-rw-r--r--tests/auto/render/shadercache/tst_shadercache.cpp293
34 files changed, 1256 insertions, 1071 deletions
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 <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <QtCore/QReadLocker>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Shader;
+
+template<class APIShader>
+class APIShaderManager
+{
+public:
+ explicit APIShaderManager()
+ {
+ }
+
+ ~APIShaderManager()
+ {
+ }
+
+ QVector<APIShader *> 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<Qt3DCore::QNodeId> &referencedShaderNodes = m_apiShaders[apiShader];
+ referencedShaderNodes.removeAll(shader->peerId());
+
+ if (referencedShaderNodes.empty()) {
+ m_abandonedShaders.push_back(apiShader);
+ m_apiShaders.remove(apiShader);
+ }
+ }
+
+ QVector<APIShader *> takeAbandonned()
+ {
+ QWriteLocker lock(&m_readWriteLock);
+ return std::move(m_abandonedShaders);
+ }
+
+ QVector<APIShader *> takeUpdated()
+ {
+ QWriteLocker lock(&m_readWriteLock);
+ return std::move(m_updatedShaders);
+ }
+
+ QVector<Qt3DCore::QNodeId> 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<QByteArray> nodeShaderCode = shaderNode->shaderCode();
+ const QVector<QByteArray> 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<Qt3DCore::QNodeId, APIShader *> m_nodeIdToAPIShader;
+ QHash<APIShader *, QVector<Qt3DCore::QNodeId>> m_apiShaders;
+
+ QVector<APIShader *> m_abandonedShaders;
+ QVector<APIShader *> 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 QVector<Render::
for (const Render::RenderCommand &c : v->commands()) {
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<Qt3DCore::QNodeId> takeShaderIdsToCleanup()
+ {
+ return std::move(m_shaderIdsToCleanup);
+ }
+
+private:
+ QVector<Qt3DCore::QNodeId> 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<QParameter, true>(QSharedPointer<Render::NodeFunctor<Render::Parameter, Render::ParameterManager> >::create(m_renderer));
q->registerBackendType<QRenderPass, true>(QSharedPointer<Render::NodeFunctor<Render::RenderPass, Render::RenderPassManager> >::create(m_renderer));
q->registerBackendType<QShaderData, true>(QSharedPointer<Render::RenderShaderDataFunctor>::create(m_renderer, m_nodeManagers));
- q->registerBackendType<QShaderProgram, true>(QSharedPointer<Render::NodeFunctor<Render::Shader, Render::ShaderManager> >::create(m_renderer));
+ q->registerBackendType<QShaderProgram, true>(QSharedPointer<Render::ShaderFunctor>::create(m_renderer, m_nodeManagers->shaderManager()));
q->registerBackendType<QShaderProgramBuilder, true>(QSharedPointer<Render::NodeFunctor<Render::ShaderBuilder, Render::ShaderBuilderManager> >::create(m_renderer));
q->registerBackendType<QTechnique, true>(QSharedPointer<Render::TechniqueFunctor>::create(m_renderer, m_nodeManagers));
q->registerBackendType<QShaderImage, true>(QSharedPointer<Render::NodeFunctor<Render::ShaderImage, Render::ShaderImageManager>>::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 <Qt3DRender/private/qshaderprogram_p.h>
#include <Qt3DRender/private/stringtoint_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DRender/private/managers_p.h>
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<int>(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<QString> Shader::uniformsNames() const
-{
- return m_uniformsNames;
-}
-
-QVector<QString> Shader::attributesNames() const
-{
- return m_attributesNames;
-}
-
-QVector<QString> Shader::uniformBlockNames() const
-{
- return m_uniformBlockNames;
-}
-
-QVector<QString> Shader::storageBlockNames() const
-{
- return m_shaderStorageBlockNames;
-}
-
-QVector<QByteArray> 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<QString, ShaderUniform> 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<QString, int> &fragOutputs)
-{
- {
- QMutexLocker lock(&m_mutex);
- m_fragOutputs = fragOutputs;
- }
- updateDNA();
-}
-
-const QHash<QString, int> 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<QString, int>::const_iterator it = m_fragOutputs.cbegin();
- QHash<QString, int>::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<ShaderUniform> &uniformsDescription)
-{
- m_uniforms = uniformsDescription;
- m_uniformsNames.resize(uniformsDescription.size());
- m_uniformsNamesIds.reserve(uniformsDescription.size());
- m_standardUniformNamesIds.reserve(5);
- QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
-
- static const QVector<int> 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<ShaderAttribute> &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<ShaderUniformBlock> &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<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
- const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
-
- QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
- const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
-
- QHash<QString, ShaderUniform> 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<ShaderStorageBlock> &shaderStorageBlockDescription)
+QVector<QByteArray> 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<ShaderStorageBlock> &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 <Qt3DRender/private/backendnode_p.h>
-#include <Qt3DRender/private/shaderparameterpack_p.h>
-#include <Qt3DRender/private/shadervariables_p.h>
-#include <Qt3DRender/qshaderprogram.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
-#include <QMutex>
+#include <Qt3DRender/qshaderprogram.h>
#include <QVector>
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<QString, int> &fragOutputs);
- const QHash<QString, int> fragOutputs() const;
-
- inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; }
- inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; }
- inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
- inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
- inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; }
-
- QVector<QString> uniformsNames() const;
- QVector<QString> attributesNames() const;
- QVector<QString> uniformBlockNames() const;
- QVector<QString> storageBlockNames() const;
QVector<QByteArray> 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<ShaderUniform> uniforms() const { return m_uniforms; }
- inline QVector<ShaderAttribute> attributes() const { return m_attributes; }
- inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; }
- inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; }
-
- QHash<QString, ShaderUniform> 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<QString> m_uniformsNames;
- QVector<int> m_uniformsNamesIds;
- QVector<int> m_standardUniformNamesIds;
- QVector<ShaderUniform> m_uniforms;
-
- QVector<QString> m_attributesNames;
- QVector<int> m_attributeNamesIds;
- QVector<ShaderAttribute> m_attributes;
-
- QVector<QString> m_uniformBlockNames;
- QVector<int> m_uniformBlockNamesIds;
- QVector<ShaderUniformBlock> m_uniformBlocks;
- QHash<int, QHash<QString, ShaderUniform> > m_uniformBlockIndexToShaderUniforms;
-
- QVector<QString> m_shaderStorageBlockNames;
- QVector<int> m_shaderStorageBlockNamesIds;
- QVector<ShaderStorageBlock> m_shaderStorageBlocks;
-
- QHash<QString, int> m_fragOutputs;
-
QVector<QByteArray> 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<ShaderUniform> &uniformsDescription);
- void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
- void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription);
- void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription);
+ QVector<Qt3DCore::QPropertyUpdatedChangePtr> 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 <QtCore/QMutexLocker>
-
-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<Qt3DCore::QNodeId> &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<Qt3DCore::QNodeId> 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<Qt3DCore::QNodeId> &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<Qt3DCore::QNodeId> &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<Qt3DCore::QNodeId> 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 <Qt3DRender/private/attachmentpack_p.h>
#include <Qt3DRender/private/qbuffer_p.h>
#include <Qt3DRender/private/renderbuffer_p.h>
+#include <Qt3DRender/private/glshader_p.h>
#include <QOpenGLShaderProgram>
#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<QOpenGLShaderProgram> 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<QShaderProgram::ShaderType>(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<Qt3DCore::QNodeId> 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<Qt3DCore::QNodeId> 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 <Qt3DRender/qmemorybarrier.h>
#include <Qt3DRender/private/handle_types_p.h>
#include <Qt3DRender/private/qgraphicsapifilter_p.h>
-#include <Qt3DRender/private/shadercache_p.h>
#include <Qt3DRender/private/uniform_p.h>
#include <Qt3DRender/private/graphicshelperinterface_p.h>
#include <Qt3DRender/private/qblitframebuffer_p.h>
@@ -89,6 +88,8 @@ class GraphicsHelperInterface;
class RenderTarget;
class AttachmentPack;
class ShaderManager;
+class GLShader;
+class GLShaderManager;
typedef QPair<QString, int> 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<QSurface *, GraphicsHelperInterface*> m_glHelpers;
GraphicsApiFilterData m_contextInfo;
- ShaderCache *m_shaderCache;
QScopedPointer<QOpenGLDebugLogger> 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 <Qt3DRender/private/qbuffer_p.h>
#include <Qt3DRender/private/renderbuffer_p.h>
#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/glshader_p.h>
#include <Qt3DRender/private/openglvertexarrayobject_p.h>
#include <QOpenGLShaderProgram>
@@ -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 <Qt3DRender/private/glbuffer_p.h>
#include <Qt3DRender/qattribute.h>
#include <Qt3DRender/private/handle_types_p.h>
-#include <Qt3DRender/private/shadercache_p.h>
#include <Qt3DRender/private/glfence_p.h>
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<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash;
QHash<Qt3DCore::QNodeId, GLuint> 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 <Qt3DRender/private/glbuffer_p.h>
#include <QOpenGLVertexArrayObject>
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 <Qt3DRender/private/glbuffer_p.h>
#include <Qt3DRender/private/glfence_p.h>
#include <Qt3DRender/private/openglvertexarrayobject_p.h>
+#include <Qt3DRender/private/glshader_p.h>
+#include <Qt3DRender/private/apishadermanager_p.h>
QT_BEGIN_NAMESPACE
@@ -93,6 +95,14 @@ public:
QHash<GLTexture *, Qt3DCore::QNodeId> texNodeIdForGLTexture;
};
+class Q_AUTOTEST_EXPORT GLShaderManager : public APIShaderManager<GLShader>
+{
+public:
+ explicit GLShaderManager()
+ : APIShaderManager<GLShader>()
+ {}
+};
+
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 <QMutexLocker>
+#include <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+GLShader::GLShader()
+ : m_isLoaded(false)
+ , m_graphicsContext(nullptr)
+{
+ m_shaderCode.resize(static_cast<int>(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<QString> GLShader::uniformsNames() const
+{
+ return m_uniformsNames;
+}
+
+QVector<QString> GLShader::attributesNames() const
+{
+ return m_attributesNames;
+}
+
+QVector<QString> GLShader::uniformBlockNames() const
+{
+ return m_uniformBlockNames;
+}
+
+QVector<QString> GLShader::storageBlockNames() const
+{
+ return m_shaderStorageBlockNames;
+}
+
+QVector<QByteArray> GLShader::shaderCode() const
+{
+ return m_shaderCode;
+}
+
+QHash<QString, ShaderUniform> 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<QString, int> &fragOutputs)
+{
+ {
+ QMutexLocker lock(&m_mutex);
+ m_fragOutputs = fragOutputs;
+ }
+// updateDNA();
+}
+
+const QHash<QString, int> GLShader::fragOutputs() const
+{
+ QMutexLocker lock(&m_mutex);
+ return m_fragOutputs;
+}
+
+void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescription)
+{
+ m_uniforms = uniformsDescription;
+ m_uniformsNames.resize(uniformsDescription.size());
+ m_uniformsNamesIds.reserve(uniformsDescription.size());
+ m_standardUniformNamesIds.reserve(5);
+ QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
+
+ static const QVector<int> 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<ShaderAttribute> &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<ShaderUniformBlock> &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<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
+ const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
+
+ QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
+ const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
+
+ QHash<QString, ShaderUniform> 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<ShaderStorageBlock> &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 <Qt3DRender/private/shadervariables_p.h>
+#include <Qt3DRender/private/shaderparameterpack_p.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <QMutex>
+
+
+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<QString, int> &fragOutputs);
+ const QHash<QString, int> fragOutputs() const;
+
+ inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; }
+ inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; }
+ inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
+ inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
+ inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; }
+
+ QVector<QString> uniformsNames() const;
+ QVector<QString> attributesNames() const;
+ QVector<QString> uniformBlockNames() const;
+ QVector<QString> storageBlockNames() const;
+
+ inline QVector<ShaderUniform> uniforms() const { return m_uniforms; }
+ inline QVector<ShaderAttribute> attributes() const { return m_attributes; }
+ inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; }
+ inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; }
+
+ QHash<QString, ShaderUniform> 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<QByteArray> shaderCode) { m_shaderCode = shaderCode; }
+ QVector<QByteArray> shaderCode() const;
+
+private:
+ bool m_isLoaded;
+ QOpenGLShaderProgram m_shader;
+ GraphicsContext *m_graphicsContext;
+
+ QVector<QString> m_uniformsNames;
+ QVector<int> m_uniformsNamesIds;
+ QVector<int> m_standardUniformNamesIds;
+ QVector<ShaderUniform> m_uniforms;
+
+ QVector<QString> m_attributesNames;
+ QVector<int> m_attributeNamesIds;
+ QVector<ShaderAttribute> m_attributes;
+
+ QVector<QString> m_uniformBlockNames;
+ QVector<int> m_uniformBlockNamesIds;
+ QVector<ShaderUniformBlock> m_uniformBlocks;
+ QHash<int, QHash<QString, ShaderUniform> > m_uniformBlockIndexToShaderUniforms;
+
+ QVector<QString> m_shaderStorageBlockNames;
+ QVector<int> m_shaderStorageBlockNamesIds;
+ QVector<ShaderStorageBlock> m_shaderStorageBlocks;
+
+ QHash<QString, int> m_fragOutputs;
+ QVector<QByteArray> m_shaderCode;
+
+ // Private so that only GraphicContext can call it
+ void initializeUniforms(const QVector<ShaderUniform> &uniformsDescription);
+ void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
+ void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription);
+ void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &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<HGeometry, HShader> VAOIdentifier;
+typedef QPair<HGeometry, Qt3DCore::QNodeId> 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<RenderStateSet>;
+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<GLShader *> shaders = m_glResourceManagers->glShaderManager()->takeActiveResources();
+ qDeleteAll(shaders);
+
// Do the same thing with VAOs
const QVector<HVao> 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 *> &renderView
if (command.m_type == RenderCommand::Draw) {
Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry);
GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer);
- Shader *shader = m_nodesManager->data<Shader, ShaderManager>(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 *> &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 *> &renderView
shader->prepareUniforms(command.m_parameterPack);
} else if (command.m_type == RenderCommand::Compute) {
- Shader *shader = m_nodesManager->data<Shader, ShaderManager>(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<Qt3DCore::QNodeId> 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<QPair<QObject *, QMouseEvent>> 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<UpdateLevelOfDetailJob> 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 <Qt3DRender/private/buffercapture_p.h>
#include <Qt3DRender/private/stringtoint_p.h>
#include <Qt3DRender/private/submissioncontext_p.h>
+#include <Qt3DRender/private/glresourcemanagers_p.h>
#include <Qt3DCore/qentity.h>
#include <QtGui/qsurface.h>
#include <algorithm>
@@ -321,7 +322,7 @@ struct AdjacentSubRangeFinder<QSortPolicy::Material>
{
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<QSortPolicy::Material>
{
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<RenderCommand> &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<Entity *> &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 QVector<Entity
command.m_stateSet->merge(m_stateSet);
command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
}
- command.m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(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 QVector<Ent
// layer component
// material/effect/technique/parameters/filters/
EntityRenderCommandData commands;
+ GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager();
commands.reserve(count);
@@ -784,7 +796,14 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent
command.m_stateSet->merge(m_stateSet);
command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
}
- command.m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(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<Shader, ShaderManager>(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/shadercache/shadercache.pro b/tests/auto/render/glshadermanager/glshadermanager.pro
index 38499588d..27aadf84f 100644
--- a/tests/auto/render/shadercache/shadercache.pro
+++ b/tests/auto/render/glshadermanager/glshadermanager.pro
@@ -1,11 +1,12 @@
TEMPLATE = app
-TARGET = tst_shadercache
+TARGET = tst_glshadermanager
QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib
CONFIG += testcase
-SOURCES += tst_shadercache.cpp
+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 <QtTest/QTest>
+#include <Qt3DRender/private/glresourcemanagers_p.h>
+#include <Qt3DCore/qnodeid.h>
+#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<Qt3DCore::QNodeId> 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<Qt3DCore::QNodeId> 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<Qt3DCore::QNodeId> 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<Qt3DRender::Render::GLShader *> 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 <private/rendercommand_p.h>
#include <testpostmanarbiter.h>
#include <testrenderer.h>
+#include <private/shader_p.h>
+#include <private/glresourcemanagers_p.h>
+#include <Qt3DRender/qshaderprogram.h>
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<RenderCommand> rawCommands;
QVector<QSortPolicy::SortType> 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<RenderCommand> rawCommands;
QVector<QSortPolicy::SortType> 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<GLShader *>(0x250),
+ reinterpret_cast<GLShader *>(0x500),
+ reinterpret_cast<GLShader *>(0x1000),
+ reinterpret_cast<GLShader *>(0x1500),
+ reinterpret_cast<GLShader *>(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<RenderCommand> 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<QVector<ProgramDNA>>("programDNAs");
+ QTest::addColumn<QVector<QShaderProgram*>>("shaders");
QTest::addColumn<QVector<ShaderParameterPack>>("rawParameters");
QTest::addColumn<QVector<ShaderParameterPack>>("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>() << ProgramDNA(883) << ProgramDNA(1584))
+ << (QVector<QShaderProgram*>() << shader1 << shader2)
<< (QVector<ShaderParameterPack>() << pack1 << pack1)
<< (QVector<ShaderParameterPack>() << pack1 << pack1);
QTest::newRow("SingleShaderMinified")
- << (QVector<ProgramDNA>() << ProgramDNA(883) << ProgramDNA(883) << ProgramDNA(883))
+ << (QVector<QShaderProgram*>() << shader1 << shader1 << shader1)
<< (QVector<ShaderParameterPack>() << pack1 << pack1 << pack1)
<< (QVector<ShaderParameterPack>() << pack1 << minifiedPack1 << minifiedPack1);
QTest::newRow("MultipleShadersMinified")
- << (QVector<ProgramDNA>() << ProgramDNA(883) << ProgramDNA(883) << ProgramDNA(883) << ProgramDNA(1584) << ProgramDNA(1584) << ProgramDNA(1584))
+ << (QVector<QShaderProgram*>() << shader1 << shader1 << shader1 << shader2 << shader2 << shader2)
<< (QVector<ShaderParameterPack>() << pack1 << pack1 << pack1 << pack1 << pack1 << pack1)
<< (QVector<ShaderParameterPack>() << pack1 << minifiedPack1 << minifiedPack1 << pack1 << minifiedPack1 << minifiedPack1);
}
void checkRenderViewUniformMinification()
{
- QFETCH(QVector<ProgramDNA>, programDNAs);
+ QFETCH(QVector<QShaderProgram*>, shaders);
QFETCH(QVector<ShaderParameterPack>, rawParameters);
QFETCH(QVector<ShaderParameterPack>, 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<RenderCommand> 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<RenderCommand> 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<RenderCommand> rawCommands;
QVector<QSortPolicy::SortType> 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<RenderCommand> rawCommands;
QVector<QSortPolicy::SortType> 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<RenderCommand> rawCommands;
QVector<QSortPolicy::SortType> 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<GLShader *>(0x250),
+ reinterpret_cast<GLShader *>(0x500),
+ reinterpret_cast<GLShader *>(0x1000),
+ reinterpret_cast<GLShader *>(0x1500),
+ reinterpret_cast<GLShader *>(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/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 <QtTest/QTest>
-#include <Qt3DRender/private/shadercache_p.h>
-#include <Qt3DCore/qnodeid.h>
-#include <QtGui/qopenglshaderprogram.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qpointer.h>
-
-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<Qt3DCore::QNodeId> 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<QOpenGLShaderProgram> 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<QOpenGLShaderProgram> progPointerA(shaderProgramA);
-
- auto dnaB = ProgramDNA(67890);
- auto nodeIdB = QNodeId::createId();
- auto shaderProgramB = new QOpenGLShaderProgram;
- QPointer<QOpenGLShaderProgram> 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"