1/****************************************************************************
2**
3** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "shadercache_p.h"
38
39#include <QtCore/QMutexLocker>
40
41QT_BEGIN_NAMESPACE
42
43namespace Qt3DRender {
44namespace Render {
45
46/*!
47 * \internal
48 *
49 * Destroys the ShaderCache and deletes all cached QOpenGLShaderPrograms
50 */
51ShaderCache::~ShaderCache()
52{
53 qDeleteAll(m_programHash);
54}
55
56/*!
57 * \internal
58 *
59 * Looks up the QOpenGLShaderProgram corresponding to \a dna. Also checks to see if the cache
60 * contains a reference to this shader program from the Shader with peerId of \a shaderPeerId.
61 * If there is no existing reference for \a shaderPeerId, one is recorded.
62 *
63 * \return A pointer to the shader program if it is cached, nullptr otherwise
64 */
65QOpenGLShaderProgram *ShaderCache::getShaderProgramAndAddRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, bool *wasPresent)
66{
67 auto shaderProgram = m_programHash.constFind(dna);
68
69 // Some callers may wish to differentiate between a result of null due to
70 // not having anything in the cache and a result of null due to the cache
71 // containing a program the shaders of which failed to compile.
72 if (wasPresent)
73 *wasPresent = shaderProgram != m_programHash.constEnd();
74
75 if (shaderProgram != m_programHash.constEnd()) {
76 // Ensure we store the fact that shaderPeerId references this shader
77 QMutexLocker lock(&m_refsMutex);
78 QVector<Qt3DCore::QNodeId> &programRefs = m_programRefs[dna];
79 auto it = std::lower_bound(programRefs.begin(), programRefs.end(), shaderPeerId);
80 if (*it != shaderPeerId)
81 programRefs.insert(it, shaderPeerId);
82
83 m_pendingRemoval.removeOne(dna);
84
85 return *shaderProgram;
86 }
87
88 return nullptr;
89}
90
91/*!
92 * \internal
93 *
94 * Inserts the \a program in the cache indexed by \a dna and adds a reference to it from
95 * \a shaderPeerId. The cache takes ownership of the \a program.
96 */
97void ShaderCache::insert(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId, QOpenGLShaderProgram *program)
98{
99 Q_ASSERT(!m_programHash.contains(dna));
100 m_programHash.insert(dna, program);
101 QMutexLocker lock(&m_refsMutex);
102 Q_ASSERT(!m_programRefs.contains(dna));
103 QVector<Qt3DCore::QNodeId> programRefs;
104 programRefs.push_back(shaderPeerId);
105 m_programRefs.insert(dna, programRefs);
106}
107
108/*!
109 * \internal
110 *
111 * Removes a reference to the shader program indexed by \a dna from the Shader with peerId of
112 * \a shaderPeerId. If this was the last reference, the dna and corresponding shader are added
113 * to a list of items to be removed by the next call to purge.
114 */
115void ShaderCache::removeRef(ProgramDNA dna, Qt3DCore::QNodeId shaderPeerId)
116{
117 QMutexLocker lock(&m_refsMutex);
118 auto it = m_programRefs.find(dna);
119 if (it != m_programRefs.end()) {
120 QVector<Qt3DCore::QNodeId> &programRefs = it.value();
121 programRefs.removeOne(shaderPeerId);
122 if (programRefs.isEmpty())
123 m_pendingRemoval.append(dna);
124 }
125}
126
127/*!
128 * \internal
129 *
130 * Iterates through a list of program dna's and checks to see if they still have no references
131 * from Shaders. If so, the dna and corresponding QOpenGLShaderProgram are removed from the cache.
132 */
133void ShaderCache::purge()
134{
135 QMutexLocker lock(&m_refsMutex);
136 for (const ProgramDNA &dna : qAsConst(m_pendingRemoval)) {
137 QVector<Qt3DCore::QNodeId> &programRefs = m_programRefs[dna];
138 if (programRefs.isEmpty()) {
139 delete m_programHash.take(dna);
140 m_programRefs.remove(dna);
141 }
142 }
143
144 m_pendingRemoval.clear();
145}
146
147/*!
148 * \internal
149 *
150 * Deletes all cached shader programs and removes any references
151 */
152void ShaderCache::clear()
153{
154 QMutexLocker lock(&m_refsMutex);
155 qDeleteAll(m_programHash);
156 m_programHash.clear();
157 m_programRefs.clear();
158 m_pendingRemoval.clear();
159}
160
161QOpenGLShaderProgram *ShaderCache::getShaderProgramForDNA(ProgramDNA dna) const
162{
163 return m_programHash.value(dna, nullptr);
164}
165
166QVector<Qt3DCore::QNodeId> ShaderCache::shaderIdsForProgram(ProgramDNA dna) const
167{
168 return m_programRefs.value(dna);
169}
170
171} // namespace Render
172} // namespace Qt3DRender
173
174QT_END_NAMESPACE
175