1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt3D module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "glshader_p.h"
41 #include <QMutexLocker>
42 #include <Qt3DRender/private/stringtoint_p.h>
43 #include <graphicscontext_p.h>
44 #include <logging_p.h>
45 #include <gllights_p.h>
46 
47 QT_BEGIN_NAMESPACE
48 
49 namespace Qt3DRender {
50 
51 namespace Render {
52 
53 namespace OpenGL {
54 
55 namespace {
56 
getLightUniformNameIds()57 QVector<int> getLightUniformNameIds()
58 {
59     QVector<int> names;
60     names.reserve(MAX_LIGHTS * 18 + 1);
61 
62     names << GLLights::LIGHT_COUNT_NAME_ID;
63     for (int i = 0; i < MAX_LIGHTS; ++i) {
64         names << GLLights::LIGHT_TYPE_NAMES[i]
65               << GLLights::LIGHT_COLOR_NAMES[i]
66               << GLLights::LIGHT_POSITION_NAMES[i]
67               << GLLights::LIGHT_INTENSITY_NAMES[i]
68               << GLLights::LIGHT_DIRECTION_NAMES[i]
69               << GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[i]
70               << GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[i]
71               << GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[i]
72               << GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[i]
73               << GLLights::LIGHT_TYPE_UNROLL_NAMES[i]
74               << GLLights::LIGHT_COLOR_UNROLL_NAMES[i]
75               << GLLights::LIGHT_POSITION_UNROLL_NAMES[i]
76               << GLLights::LIGHT_INTENSITY_UNROLL_NAMES[i]
77               << GLLights::LIGHT_DIRECTION_UNROLL_NAMES[i]
78               << GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[i]
79               << GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[i]
80               << GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[i]
81               << GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[i];
82     }
83 
84     return names;
85 }
86 
87 template<typename Vector>
fastContains(const Vector & v,int value)88 bool fastContains(const Vector &v, int value)
89 {
90     return std::binary_search(v.cbegin(), v.cend(), value);
91 }
92 
93 }
94 
GLShader()95 GLShader::GLShader()
96     : m_isLoaded(false)
97     , m_graphicsContext(nullptr)
98     , m_parameterPackSize(0)
99     , m_hasActiveVariables(false)
100 {
101     m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
102 }
103 
~GLShader()104 GLShader::~GLShader()
105 {
106     if (m_contextConnection)
107         QObject::disconnect(m_contextConnection);
108 }
109 
setGraphicsContext(GraphicsContext * context)110 void GLShader::setGraphicsContext(GraphicsContext *context)
111 {
112     QMutexLocker lock(&m_mutex);
113     m_graphicsContext = context;
114     if (m_graphicsContext) {
115         m_contextConnection = QObject::connect(m_graphicsContext->openGLContext(),
116                                                &QOpenGLContext::aboutToBeDestroyed,
117                                                [this] { setGraphicsContext(nullptr); });
118     }
119 }
120 
graphicsContext()121 GraphicsContext *GLShader::graphicsContext()
122 {
123     QMutexLocker lock(&m_mutex);
124     return m_graphicsContext;
125 }
126 
127 
uniformsNames() const128 QVector<QString> GLShader::uniformsNames() const
129 {
130     return m_uniformsNames;
131 }
132 
attributesNames() const133 QVector<QString> GLShader::attributesNames() const
134 {
135     return m_attributesNames;
136 }
137 
uniformBlockNames() const138 QVector<QString> GLShader::uniformBlockNames() const
139 {
140     return m_uniformBlockNames;
141 }
142 
storageBlockNames() const143 QVector<QString> GLShader::storageBlockNames() const
144 {
145     return m_shaderStorageBlockNames;
146 }
147 
shaderCode() const148 QVector<QByteArray> GLShader::shaderCode() const
149 {
150     return m_shaderCode;
151 }
152 
activeUniformsForUniformBlock(int blockIndex) const153 QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const
154 {
155     return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
156 }
157 
uniformBlockForBlockIndex(int blockIndex) const158 ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
159 {
160     for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
161         if (m_uniformBlocks[i].m_index == blockIndex) {
162             return m_uniformBlocks[i];
163         }
164     }
165     return ShaderUniformBlock();
166 }
167 
uniformBlockForBlockNameId(int blockNameId) const168 ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
169 {
170     for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
171         if (m_uniformBlocks[i].m_nameId == blockNameId) {
172             return m_uniformBlocks[i];
173         }
174     }
175     return ShaderUniformBlock();
176 }
177 
uniformBlockForBlockName(const QString & blockName) const178 ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept
179 {
180     for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
181         if (m_uniformBlocks[i].m_name == blockName) {
182             return m_uniformBlocks[i];
183         }
184     }
185     return ShaderUniformBlock();
186 }
187 
storageBlockForBlockIndex(int blockIndex) const188 ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept
189 {
190     for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
191         if (m_shaderStorageBlocks[i].m_index == blockIndex)
192             return m_shaderStorageBlocks[i];
193     }
194     return ShaderStorageBlock();
195 }
196 
storageBlockForBlockNameId(int blockNameId) const197 ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept
198 {
199     for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
200         if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
201             return m_shaderStorageBlocks[i];
202     }
203     return ShaderStorageBlock();
204 }
205 
storageBlockForBlockName(const QString & blockName) const206 ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept
207 {
208     for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
209         if (m_shaderStorageBlocks[i].m_name == blockName)
210             return m_shaderStorageBlocks[i];
211     }
212     return ShaderStorageBlock();
213 }
214 
categorizeVariable(int nameId) const215 GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept
216 {
217     if (fastContains(m_uniformsNamesIds, nameId))
218         return ParameterKind::Uniform;
219     if (fastContains(m_uniformBlockNamesIds, nameId))
220         return ParameterKind::UBO;
221     if (fastContains(m_shaderStorageBlockNamesIds, nameId))
222         return ParameterKind::SSBO;
223     return ParameterKind::Struct;
224 }
225 
hasUniform(int nameId) const226 bool GLShader::hasUniform(int nameId) const noexcept
227 {
228     return m_uniformsNamesIds.contains(nameId);
229 }
230 
prepareUniforms(ShaderParameterPack & pack)231 void GLShader::prepareUniforms(ShaderParameterPack &pack)
232 {
233     const PackUniformHash &values = pack.uniforms();
234 
235     auto it = values.keys.cbegin();
236     const auto end = values.keys.cend();
237 
238     const int shaderUniformsCount = m_uniforms.size();
239     const auto uIt = m_uniforms.cbegin();
240 
241     while (it != end) {
242         // Find if there's a uniform with the same name id
243 
244         int i = 0;
245         const int targetNameId = *it;
246         while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId)
247             ++i;
248 
249         if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId)
250             pack.setSubmissionUniformIndex(i);
251 
252         ++it;
253     }
254 }
255 
setFragOutputs(const QHash<QString,int> & fragOutputs)256 void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
257 {
258     {
259         QMutexLocker lock(&m_mutex);
260         m_fragOutputs = fragOutputs;
261     }
262 }
263 
fragOutputs() const264 const QHash<QString, int> GLShader::fragOutputs() const
265 {
266     QMutexLocker lock(&m_mutex);
267     return m_fragOutputs;
268 }
269 
initializeUniforms(const QVector<ShaderUniform> & uniformsDescription)270 void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescription)
271 {
272     m_uniforms = uniformsDescription;
273     m_uniformsNames.resize(uniformsDescription.size());
274     m_uniformsNamesIds.reserve(uniformsDescription.size());
275     m_standardUniformNamesIds.reserve(5);
276     m_lightUniformsNamesIds.reserve(MAX_LIGHTS * 8 + 1);
277     QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
278 
279     static const QVector<int> standardUniformNameIds = {
280         Shader::modelMatrixNameId,
281         Shader::viewMatrixNameId,
282         Shader::projectionMatrixNameId,
283         Shader::modelViewMatrixNameId,
284         Shader::viewProjectionMatrixNameId,
285         Shader::modelViewProjectionNameId,
286         Shader::mvpNameId,
287         Shader::inverseModelMatrixNameId,
288         Shader::inverseViewMatrixNameId,
289         Shader::inverseProjectionMatrixNameId,
290         Shader::inverseModelViewNameId,
291         Shader::inverseViewProjectionMatrixNameId,
292         Shader::inverseModelViewProjectionNameId,
293         Shader::modelNormalMatrixNameId,
294         Shader::modelViewNormalNameId,
295         Shader::viewportMatrixNameId,
296         Shader::inverseViewportMatrixNameId,
297         Shader::aspectRatioNameId,
298         Shader::exposureNameId,
299         Shader::gammaNameId,
300         Shader::timeNameId,
301         Shader::eyePositionNameId,
302         Shader::skinningPaletteNameId,
303     };
304 
305     static const QVector<int> lightUniformNameIds = getLightUniformNameIds();
306 
307     for (int i = 0, m = uniformsDescription.size(); i < m; i++) {
308         m_uniformsNames[i] = m_uniforms[i].m_name;
309         const int nameId = StringToInt::lookupId(m_uniformsNames[i]);
310         m_uniforms[i].m_nameId = nameId;
311 
312         // Is the uniform a Qt3D "Standard" uniform, a light uniform or a user defined one?
313         if (standardUniformNameIds.contains(nameId))
314             m_standardUniformNamesIds.push_back(nameId);
315         else if (lightUniformNameIds.contains(nameId))
316             m_lightUniformsNamesIds.push_back(nameId);
317         else
318             m_uniformsNamesIds.push_back(nameId);
319 
320         if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block
321             qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex;
322             activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]);
323         }
324     }
325     m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock);
326 
327     m_parameterPackSize += m_standardUniformNamesIds.size() + m_lightUniformsNamesIds.size() + m_uniformsNamesIds.size();
328     m_hasActiveVariables |= (m_parameterPackSize > 0);
329 
330     // Sort by ascending order to make contains check faster
331     std::sort(m_uniformsNamesIds.begin(), m_uniformsNamesIds.end());
332     std::sort(m_lightUniformsNamesIds.begin(), m_lightUniformsNamesIds.end());
333     std::sort(m_standardUniformNamesIds.begin(), m_standardUniformNamesIds.end());
334     std::sort(m_uniforms.begin(), m_uniforms.end(),
335               [] (const ShaderUniform &a, const ShaderUniform &b) {
336         return a.m_nameId < b.m_nameId;
337     });
338 }
339 
initializeAttributes(const QVector<ShaderAttribute> & attributesDescription)340 void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription)
341 {
342     m_attributes = attributesDescription;
343     m_attributesNames.resize(attributesDescription.size());
344     m_attributeNamesIds.resize(attributesDescription.size());
345     for (int i = 0, m = attributesDescription.size(); i < m; i++) {
346         m_attributesNames[i] = attributesDescription[i].m_name;
347         m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]);
348         m_attributeNamesIds[i] = m_attributes[i].m_nameId;
349         qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
350     }
351     m_hasActiveVariables |= (m_attributeNamesIds.size() > 0);
352 }
353 
initializeUniformBlocks(const QVector<ShaderUniformBlock> & uniformBlockDescription)354 void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription)
355 {
356     m_uniformBlocks = uniformBlockDescription;
357     m_uniformBlockNames.resize(uniformBlockDescription.size());
358     m_uniformBlockNamesIds.resize(uniformBlockDescription.size());
359     for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) {
360         m_uniformBlockNames[i] = m_uniformBlocks[i].m_name;
361         m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]);
362         m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i];
363         qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}";
364 
365         // Find all active uniforms for the shader block
366         QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
367         const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
368 
369         QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
370         const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
371 
372         QHash<QString, ShaderUniform> activeUniformsInBlock;
373 
374         while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) {
375             if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) {
376                 QString uniformName = *uniformNamesIt;
377                 if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(m_uniformBlockNames[i]))
378                     uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt;
379                 activeUniformsInBlock.insert(uniformName, *uniformsIt);
380                 qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex;
381             }
382             ++uniformsIt;
383             ++uniformNamesIt;
384         }
385         m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock);
386     }
387 
388     m_parameterPackSize += m_uniformsNamesIds.size();
389     m_hasActiveVariables |= (m_parameterPackSize > 0);
390 
391     // Sort by ascending order to make contains check faster
392     std::sort(m_uniformBlockNamesIds.begin(), m_uniformBlockNamesIds.end());
393 }
394 
initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> & shaderStorageBlockDescription)395 void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription)
396 {
397     m_shaderStorageBlocks = shaderStorageBlockDescription;
398     m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size());
399     m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size());
400 
401     for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) {
402         m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name;
403         m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]);
404         m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
405         qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
406     }
407 
408     m_parameterPackSize += m_shaderStorageBlockNamesIds.size();
409     m_hasActiveVariables |= (m_parameterPackSize > 0);
410 
411     // Sort by ascending order to make contains check faster
412     std::sort(m_shaderStorageBlockNamesIds.begin(), m_shaderStorageBlockNamesIds.end());
413 }
414 
415 } // OpenGL
416 
417 } // Render
418 
419 } // Qt3DRender
420 
421 QT_END_NAMESPACE
422