1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "glshader_p.h"
5#include <QMutexLocker>
6#include <Qt3DRender/private/stringtoint_p.h>
7#include <graphicscontext_p.h>
8#include <logging_p.h>
9#include <gllights_p.h>
10#include <Qt3DCore/private/vector_helper_p.h>
11
12QT_BEGIN_NAMESPACE
13
14namespace Qt3DRender {
15
16namespace Render {
17
18namespace OpenGL {
19
20namespace {
21
22std::vector<int> getLightUniformNameIds()
23{
24 std::vector<int> names;
25 names.reserve(MAX_LIGHTS * 18 + 1);
26
27 names.push_back(x: GLLights::LIGHT_COUNT_NAME_ID);
28 for (int i = 0; i < MAX_LIGHTS; ++i) {
29 names.push_back(x: GLLights::LIGHT_TYPE_NAMES[i]);
30 names.push_back(x: GLLights::LIGHT_COLOR_NAMES[i]);
31 names.push_back(x: GLLights::LIGHT_POSITION_NAMES[i]);
32 names.push_back(x: GLLights::LIGHT_INTENSITY_NAMES[i]);
33 names.push_back(x: GLLights::LIGHT_DIRECTION_NAMES[i]);
34 names.push_back(x: GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[i]);
35 names.push_back(x: GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[i]);
36 names.push_back(x: GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[i]);
37 names.push_back(x: GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[i]);
38 names.push_back(x: GLLights::LIGHT_TYPE_UNROLL_NAMES[i]);
39 names.push_back(x: GLLights::LIGHT_COLOR_UNROLL_NAMES[i]);
40 names.push_back(x: GLLights::LIGHT_POSITION_UNROLL_NAMES[i]);
41 names.push_back(x: GLLights::LIGHT_INTENSITY_UNROLL_NAMES[i]);
42 names.push_back(x: GLLights::LIGHT_DIRECTION_UNROLL_NAMES[i]);
43 names.push_back(x: GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[i]);
44 names.push_back(x: GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[i]);
45 names.push_back(x: GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[i]);
46 names.push_back(x: GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[i]);
47 }
48
49 return names;
50}
51
52template<typename Vector>
53bool fastContains(const Vector &v, int value)
54{
55 return std::binary_search(v.cbegin(), v.cend(), value);
56}
57
58}
59
60GLShader::GLShader()
61 : m_isLoaded(false)
62 , m_graphicsContext(nullptr)
63 , m_parameterPackSize(0)
64 , m_hasActiveVariables(false)
65{
66 m_shaderCode.resize(new_size: static_cast<int>(QShaderProgram::Compute) + 1);
67}
68
69GLShader::~GLShader()
70{
71 if (m_contextConnection)
72 QObject::disconnect(m_contextConnection);
73}
74
75void GLShader::setGraphicsContext(GraphicsContext *context)
76{
77 QMutexLocker lock(&m_mutex);
78 m_graphicsContext = context;
79 if (m_graphicsContext) {
80 m_contextConnection = QObject::connect(sender: m_graphicsContext->openGLContext(),
81 signal: &QOpenGLContext::aboutToBeDestroyed,
82 slot: [this] { setGraphicsContext(nullptr); });
83 }
84}
85
86GraphicsContext *GLShader::graphicsContext()
87{
88 QMutexLocker lock(&m_mutex);
89 return m_graphicsContext;
90}
91
92
93const std::vector<QString> &GLShader::uniformsNames() const
94{
95 return m_uniformsNames;
96}
97
98const std::vector<QString> &GLShader::attributesNames() const
99{
100 return m_attributesNames;
101}
102
103const std::vector<QString> &GLShader::uniformBlockNames() const
104{
105 return m_uniformBlockNames;
106}
107
108const std::vector<QString> &GLShader::storageBlockNames() const
109{
110 return m_shaderStorageBlockNames;
111}
112
113const std::vector<QByteArray> &GLShader::shaderCode() const
114{
115 return m_shaderCode;
116}
117
118QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const
119{
120 return m_uniformBlockIndexToShaderUniforms.value(key: blockIndex);
121}
122
123ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
124{
125 for (size_t i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
126 if (m_uniformBlocks[i].m_index == blockIndex) {
127 return m_uniformBlocks[i];
128 }
129 }
130 return ShaderUniformBlock();
131}
132
133ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
134{
135 for (size_t i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
136 if (m_uniformBlocks[i].m_nameId == blockNameId) {
137 return m_uniformBlocks[i];
138 }
139 }
140 return ShaderUniformBlock();
141}
142
143ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept
144{
145 for (size_t i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
146 if (m_uniformBlocks[i].m_name == blockName) {
147 return m_uniformBlocks[i];
148 }
149 }
150 return ShaderUniformBlock();
151}
152
153ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept
154{
155 for (size_t i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
156 if (m_shaderStorageBlocks[i].m_index == blockIndex)
157 return m_shaderStorageBlocks[i];
158 }
159 return ShaderStorageBlock();
160}
161
162ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept
163{
164 for (size_t i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
165 if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
166 return m_shaderStorageBlocks[i];
167 }
168 return ShaderStorageBlock();
169}
170
171ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept
172{
173 for (size_t i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
174 if (m_shaderStorageBlocks[i].m_name == blockName)
175 return m_shaderStorageBlocks[i];
176 }
177 return ShaderStorageBlock();
178}
179
180GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept
181{
182 if (fastContains(v: m_uniformsNamesIds, value: nameId))
183 return ParameterKind::Uniform;
184 if (fastContains(v: m_uniformBlockNamesIds, value: nameId))
185 return ParameterKind::UBO;
186 if (fastContains(v: m_shaderStorageBlockNamesIds, value: nameId))
187 return ParameterKind::SSBO;
188 return ParameterKind::Struct;
189}
190
191bool GLShader::hasUniform(int nameId) const noexcept
192{
193 return Qt3DCore::contains(destination: m_uniformsNamesIds, element: nameId);
194}
195
196void GLShader::prepareUniforms(ShaderParameterPack &pack)
197{
198 const PackUniformHash &values = pack.uniforms();
199
200 auto it = values.keys.cbegin();
201 const auto end = values.keys.cend();
202
203 const int shaderUniformsCount = int(m_uniforms.size());
204 const auto uIt = m_uniforms.cbegin();
205
206 while (it != end) {
207 // Find if there's a uniform with the same name id
208
209 int i = 0;
210 const int targetNameId = *it;
211 while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId)
212 ++i;
213
214 if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId)
215 pack.setSubmissionUniformIndex(i);
216
217 ++it;
218 }
219}
220
221void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
222{
223 {
224 QMutexLocker lock(&m_mutex);
225 m_fragOutputs = fragOutputs;
226 }
227}
228
229const QHash<QString, int> GLShader::fragOutputs() const
230{
231 QMutexLocker lock(&m_mutex);
232 return m_fragOutputs;
233}
234
235void GLShader::initializeUniforms(const std::vector<ShaderUniform> &uniformsDescription)
236{
237 m_uniforms = uniformsDescription;
238 m_uniformsNames.resize(new_size: uniformsDescription.size());
239 m_uniformsNamesIds.reserve(n: uniformsDescription.size());
240 m_standardUniformNamesIds.reserve(n: 5);
241 m_lightUniformsNamesIds.reserve(MAX_LIGHTS * 8 + 1);
242 QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
243
244 static const std::vector<int> standardUniformNameIds = {
245 Shader::modelMatrixNameId,
246 Shader::viewMatrixNameId,
247 Shader::projectionMatrixNameId,
248 Shader::modelViewMatrixNameId,
249 Shader::viewProjectionMatrixNameId,
250 Shader::modelViewProjectionNameId,
251 Shader::mvpNameId,
252 Shader::inverseModelMatrixNameId,
253 Shader::inverseViewMatrixNameId,
254 Shader::inverseProjectionMatrixNameId,
255 Shader::inverseModelViewNameId,
256 Shader::inverseViewProjectionMatrixNameId,
257 Shader::inverseModelViewProjectionNameId,
258 Shader::modelNormalMatrixNameId,
259 Shader::modelViewNormalNameId,
260 Shader::viewportMatrixNameId,
261 Shader::inverseViewportMatrixNameId,
262 Shader::aspectRatioNameId,
263 Shader::exposureNameId,
264 Shader::gammaNameId,
265 Shader::timeNameId,
266 Shader::eyePositionNameId,
267 Shader::skinningPaletteNameId,
268 };
269
270 static const std::vector<int> lightUniformNameIds = getLightUniformNameIds();
271
272 for (size_t i = 0, m = uniformsDescription.size(); i < m; i++) {
273 m_uniformsNames[i] = m_uniforms[i].m_name;
274 const int nameId = StringToInt::lookupId(str: m_uniformsNames[i]);
275 m_uniforms[i].m_nameId = nameId;
276
277 // Is the uniform a Qt3D "Standard" uniform, a light uniform or a user defined one?
278 if (Qt3DCore::contains(destination: standardUniformNameIds, element: nameId))
279 m_standardUniformNamesIds.push_back(x: nameId);
280 else if (Qt3DCore::contains(destination: lightUniformNameIds, element: nameId))
281 m_lightUniformsNamesIds.push_back(x: nameId);
282 else
283 m_uniformsNamesIds.push_back(x: nameId);
284
285 if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block
286 qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex;
287 activeUniformsInDefaultBlock.insert(key: uniformsDescription[i].m_name, value: uniformsDescription[i]);
288 }
289 }
290 m_uniformBlockIndexToShaderUniforms.insert(key: -1, value: activeUniformsInDefaultBlock);
291
292 m_parameterPackSize += int(m_standardUniformNamesIds.size()) + int(m_lightUniformsNamesIds.size()) + int(m_uniformsNamesIds.size());
293 m_hasActiveVariables |= (m_parameterPackSize > 0);
294
295 // Sort by ascending order to make contains check faster
296 std::sort(first: m_uniformsNamesIds.begin(), last: m_uniformsNamesIds.end());
297 std::sort(first: m_lightUniformsNamesIds.begin(), last: m_lightUniformsNamesIds.end());
298 std::sort(first: m_standardUniformNamesIds.begin(), last: m_standardUniformNamesIds.end());
299 std::sort(first: m_uniforms.begin(), last: m_uniforms.end(),
300 comp: [] (const ShaderUniform &a, const ShaderUniform &b) {
301 return a.m_nameId < b.m_nameId;
302 });
303}
304
305void GLShader::initializeAttributes(const std::vector<ShaderAttribute> &attributesDescription)
306{
307 m_attributes = attributesDescription;
308 m_attributesNames.resize(new_size: attributesDescription.size());
309 m_attributeNamesIds.resize(new_size: attributesDescription.size());
310 for (int i = 0, m = int(attributesDescription.size()); i < m; i++) {
311 m_attributesNames[i] = attributesDescription[i].m_name;
312 m_attributes[i].m_nameId = StringToInt::lookupId(str: m_attributesNames[i]);
313 m_attributeNamesIds[i] = m_attributes[i].m_nameId;
314 qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
315 }
316 m_hasActiveVariables |= (m_attributeNamesIds.size() > 0);
317}
318
319void GLShader::initializeUniformBlocks(const std::vector<ShaderUniformBlock> &uniformBlockDescription)
320{
321 m_uniformBlocks = uniformBlockDescription;
322 m_uniformBlockNames.resize(new_size: uniformBlockDescription.size());
323 m_uniformBlockNamesIds.resize(new_size: uniformBlockDescription.size());
324 for (int i = 0, m = int(uniformBlockDescription.size()); i < m; ++i) {
325 m_uniformBlockNames[i] = m_uniformBlocks[i].m_name;
326 m_uniformBlockNamesIds[i] = StringToInt::lookupId(str: m_uniformBlockNames[i]);
327 m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i];
328 qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}";
329
330 // Find all active uniforms for the shader block
331 std::vector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
332 const std::vector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
333
334 std::vector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
335 const std::vector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
336
337 QHash<QString, ShaderUniform> activeUniformsInBlock;
338
339 while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) {
340 if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) {
341 QString uniformName = *uniformNamesIt;
342 if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(s: m_uniformBlockNames[i]))
343 uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt;
344 activeUniformsInBlock.insert(key: uniformName, value: *uniformsIt);
345 qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex;
346 }
347 ++uniformsIt;
348 ++uniformNamesIt;
349 }
350 m_uniformBlockIndexToShaderUniforms.insert(key: uniformBlockDescription[i].m_index, value: activeUniformsInBlock);
351 }
352
353 m_parameterPackSize += int(m_uniformsNamesIds.size());
354 m_hasActiveVariables |= (m_parameterPackSize > 0);
355
356 // Sort by ascending order to make contains check faster
357 std::sort(first: m_uniformBlockNamesIds.begin(), last: m_uniformBlockNamesIds.end());
358}
359
360void GLShader::initializeShaderStorageBlocks(const std::vector<ShaderStorageBlock> &shaderStorageBlockDescription)
361{
362 m_shaderStorageBlocks = shaderStorageBlockDescription;
363 m_shaderStorageBlockNames.resize(new_size: shaderStorageBlockDescription.size());
364 m_shaderStorageBlockNamesIds.resize(new_size: shaderStorageBlockDescription.size());
365
366 for (int i = 0, m = int(shaderStorageBlockDescription.size()); i < m; ++i) {
367 m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name;
368 m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(str: m_shaderStorageBlockNames[i]);
369 m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
370 qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
371 }
372
373 m_parameterPackSize += int(m_shaderStorageBlockNamesIds.size());
374 m_hasActiveVariables |= (m_parameterPackSize > 0);
375
376 // Sort by ascending order to make contains check faster
377 std::sort(first: m_shaderStorageBlockNamesIds.begin(), last: m_shaderStorageBlockNamesIds.end());
378}
379
380} // OpenGL
381
382} // Render
383
384} // Qt3DRender
385
386QT_END_NAMESPACE
387

source code of qt3d/src/plugins/renderers/opengl/renderer/glshader.cpp