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 | |
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> |
88 | bool fastContains(const Vector &v, int value) |
89 | { |
90 | return std::binary_search(v.cbegin(), v.cend(), value); |
91 | } |
92 | |
93 | } |
94 | |
95 | GLShader::GLShader() |
96 | : m_isLoaded(false) |
97 | , m_graphicsContext(nullptr) |
98 | , m_parameterPackSize(0) |
99 | , m_hasActiveVariables(false) |
100 | { |
101 | m_shaderCode.resize(asize: static_cast<int>(QShaderProgram::Compute) + 1); |
102 | } |
103 | |
104 | GLShader::~GLShader() |
105 | { |
106 | if (m_contextConnection) |
107 | QObject::disconnect(m_contextConnection); |
108 | } |
109 | |
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(sender: m_graphicsContext->openGLContext(), |
116 | signal: &QOpenGLContext::aboutToBeDestroyed, |
117 | slot: [this] { setGraphicsContext(nullptr); }); |
118 | } |
119 | } |
120 | |
121 | GraphicsContext *GLShader::graphicsContext() |
122 | { |
123 | QMutexLocker lock(&m_mutex); |
124 | return m_graphicsContext; |
125 | } |
126 | |
127 | |
128 | QVector<QString> GLShader::uniformsNames() const |
129 | { |
130 | return m_uniformsNames; |
131 | } |
132 | |
133 | QVector<QString> GLShader::attributesNames() const |
134 | { |
135 | return m_attributesNames; |
136 | } |
137 | |
138 | QVector<QString> GLShader::uniformBlockNames() const |
139 | { |
140 | return m_uniformBlockNames; |
141 | } |
142 | |
143 | QVector<QString> GLShader::storageBlockNames() const |
144 | { |
145 | return m_shaderStorageBlockNames; |
146 | } |
147 | |
148 | QVector<QByteArray> GLShader::shaderCode() const |
149 | { |
150 | return m_shaderCode; |
151 | } |
152 | |
153 | QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const |
154 | { |
155 | return m_uniformBlockIndexToShaderUniforms.value(akey: blockIndex); |
156 | } |
157 | |
158 | 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 | |
168 | 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 | |
178 | 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 | |
188 | 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 | |
197 | 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 | |
206 | 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 | |
215 | GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept |
216 | { |
217 | if (fastContains(v: m_uniformsNamesIds, value: nameId)) |
218 | return ParameterKind::Uniform; |
219 | if (fastContains(v: m_uniformBlockNamesIds, value: nameId)) |
220 | return ParameterKind::UBO; |
221 | if (fastContains(v: m_shaderStorageBlockNamesIds, value: nameId)) |
222 | return ParameterKind::SSBO; |
223 | return ParameterKind::Struct; |
224 | } |
225 | |
226 | bool GLShader::hasUniform(int nameId) const noexcept |
227 | { |
228 | return m_uniformsNamesIds.contains(t: nameId); |
229 | } |
230 | |
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 | |
256 | void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs) |
257 | { |
258 | { |
259 | QMutexLocker lock(&m_mutex); |
260 | m_fragOutputs = fragOutputs; |
261 | } |
262 | } |
263 | |
264 | const QHash<QString, int> GLShader::fragOutputs() const |
265 | { |
266 | QMutexLocker lock(&m_mutex); |
267 | return m_fragOutputs; |
268 | } |
269 | |
270 | void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescription) |
271 | { |
272 | m_uniforms = uniformsDescription; |
273 | m_uniformsNames.resize(asize: uniformsDescription.size()); |
274 | m_uniformsNamesIds.reserve(asize: uniformsDescription.size()); |
275 | m_standardUniformNamesIds.reserve(asize: 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(str: 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(t: nameId)) |
314 | m_standardUniformNamesIds.push_back(t: nameId); |
315 | else if (lightUniformNameIds.contains(t: nameId)) |
316 | m_lightUniformsNamesIds.push_back(t: nameId); |
317 | else |
318 | m_uniformsNamesIds.push_back(t: 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(akey: uniformsDescription[i].m_name, avalue: uniformsDescription[i]); |
323 | } |
324 | } |
325 | m_uniformBlockIndexToShaderUniforms.insert(akey: -1, avalue: 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(first: m_uniformsNamesIds.begin(), last: m_uniformsNamesIds.end()); |
332 | std::sort(first: m_lightUniformsNamesIds.begin(), last: m_lightUniformsNamesIds.end()); |
333 | std::sort(first: m_standardUniformNamesIds.begin(), last: m_standardUniformNamesIds.end()); |
334 | std::sort(first: m_uniforms.begin(), last: m_uniforms.end(), |
335 | comp: [] (const ShaderUniform &a, const ShaderUniform &b) { |
336 | return a.m_nameId < b.m_nameId; |
337 | }); |
338 | } |
339 | |
340 | void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription) |
341 | { |
342 | m_attributes = attributesDescription; |
343 | m_attributesNames.resize(asize: attributesDescription.size()); |
344 | m_attributeNamesIds.resize(asize: 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(str: 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 | |
354 | void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription) |
355 | { |
356 | m_uniformBlocks = uniformBlockDescription; |
357 | m_uniformBlockNames.resize(asize: uniformBlockDescription.size()); |
358 | m_uniformBlockNamesIds.resize(asize: 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(str: 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(s: m_uniformBlockNames[i])) |
378 | uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt; |
379 | activeUniformsInBlock.insert(akey: uniformName, avalue: *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(akey: uniformBlockDescription[i].m_index, avalue: 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(first: m_uniformBlockNamesIds.begin(), last: m_uniformBlockNamesIds.end()); |
393 | } |
394 | |
395 | void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription) |
396 | { |
397 | m_shaderStorageBlocks = shaderStorageBlockDescription; |
398 | m_shaderStorageBlockNames.resize(asize: shaderStorageBlockDescription.size()); |
399 | m_shaderStorageBlockNamesIds.resize(asize: 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(str: 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(first: m_shaderStorageBlockNamesIds.begin(), last: m_shaderStorageBlockNamesIds.end()); |
413 | } |
414 | |
415 | } // OpenGL |
416 | |
417 | } // Render |
418 | |
419 | } // Qt3DRender |
420 | |
421 | QT_END_NAMESPACE |
422 | |