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