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 context: m_graphicsContext->openGLContext(),
83 slot: [this] { setGraphicsContext(nullptr); });
84 }
85}
86
87GraphicsContext *GLShader::graphicsContext()
88{
89 QMutexLocker lock(&m_mutex);
90 return m_graphicsContext;
91}
92
93
94const std::vector<QString> &GLShader::uniformsNames() const
95{
96 return m_uniformsNames;
97}
98
99const std::vector<QString> &GLShader::attributesNames() const
100{
101 return m_attributesNames;
102}
103
104const std::vector<QString> &GLShader::uniformBlockNames() const
105{
106 return m_uniformBlockNames;
107}
108
109const std::vector<QString> &GLShader::storageBlockNames() const
110{
111 return m_shaderStorageBlockNames;
112}
113
114const std::vector<QByteArray> &GLShader::shaderCode() const
115{
116 return m_shaderCode;
117}
118
119QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const
120{
121 return m_uniformBlockIndexToShaderUniforms.value(key: blockIndex);
122}
123
124ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
125{
126 for (size_t i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
127 if (m_uniformBlocks[i].m_index == blockIndex) {
128 return m_uniformBlocks[i];
129 }
130 }
131 return ShaderUniformBlock();
132}
133
134ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
135{
136 for (size_t i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
137 if (m_uniformBlocks[i].m_nameId == blockNameId) {
138 return m_uniformBlocks[i];
139 }
140 }
141 return ShaderUniformBlock();
142}
143
144ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept
145{
146 for (size_t i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
147 if (m_uniformBlocks[i].m_name == blockName) {
148 return m_uniformBlocks[i];
149 }
150 }
151 return ShaderUniformBlock();
152}
153
154ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept
155{
156 for (size_t i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
157 if (m_shaderStorageBlocks[i].m_index == blockIndex)
158 return m_shaderStorageBlocks[i];
159 }
160 return ShaderStorageBlock();
161}
162
163ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept
164{
165 for (size_t i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
166 if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
167 return m_shaderStorageBlocks[i];
168 }
169 return ShaderStorageBlock();
170}
171
172ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept
173{
174 for (size_t i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
175 if (m_shaderStorageBlocks[i].m_name == blockName)
176 return m_shaderStorageBlocks[i];
177 }
178 return ShaderStorageBlock();
179}
180
181GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept
182{
183 if (fastContains(v: m_uniformsNamesIds, value: nameId))
184 return ParameterKind::Uniform;
185 if (fastContains(v: m_uniformBlockNamesIds, value: nameId))
186 return ParameterKind::UBO;
187 if (fastContains(v: m_shaderStorageBlockNamesIds, value: nameId))
188 return ParameterKind::SSBO;
189 return ParameterKind::Struct;
190}
191
192bool GLShader::hasUniform(int nameId) const noexcept
193{
194 return Qt3DCore::contains(destination: m_uniformsNamesIds, element: nameId);
195}
196
197void GLShader::prepareUniforms(ShaderParameterPack &pack)
198{
199 const PackUniformHash &values = pack.uniforms();
200
201 auto it = values.keys.cbegin();
202 const auto end = values.keys.cend();
203
204 const int shaderUniformsCount = int(m_uniforms.size());
205 const auto uIt = m_uniforms.cbegin();
206
207 while (it != end) {
208 // Find if there's a uniform with the same name id
209
210 int i = 0;
211 const int targetNameId = *it;
212 while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId)
213 ++i;
214
215 if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId)
216 pack.setSubmissionUniformIndex(i);
217
218 ++it;
219 }
220}
221
222void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
223{
224 {
225 QMutexLocker lock(&m_mutex);
226 m_fragOutputs = fragOutputs;
227 }
228}
229
230const QHash<QString, int> GLShader::fragOutputs() const
231{
232 QMutexLocker lock(&m_mutex);
233 return m_fragOutputs;
234}
235
236void GLShader::initializeUniforms(const std::vector<ShaderUniform> &uniformsDescription)
237{
238 m_uniforms = uniformsDescription;
239 m_uniformsNames.resize(new_size: uniformsDescription.size());
240 m_uniformsNamesIds.reserve(n: uniformsDescription.size());
241 m_standardUniformNamesIds.reserve(n: 5);
242 m_lightUniformsNamesIds.reserve(MAX_LIGHTS * 8 + 1);
243 QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
244
245 static const std::vector<int> standardUniformNameIds = {
246 Shader::modelMatrixNameId,
247 Shader::viewMatrixNameId,
248 Shader::projectionMatrixNameId,
249 Shader::modelViewMatrixNameId,
250 Shader::viewProjectionMatrixNameId,
251 Shader::modelViewProjectionNameId,
252 Shader::mvpNameId,
253 Shader::inverseModelMatrixNameId,
254 Shader::inverseViewMatrixNameId,
255 Shader::inverseProjectionMatrixNameId,
256 Shader::inverseModelViewNameId,
257 Shader::inverseViewProjectionMatrixNameId,
258 Shader::inverseModelViewProjectionNameId,
259 Shader::modelNormalMatrixNameId,
260 Shader::modelViewNormalNameId,
261 Shader::viewportMatrixNameId,
262 Shader::inverseViewportMatrixNameId,
263 Shader::aspectRatioNameId,
264 Shader::exposureNameId,
265 Shader::gammaNameId,
266 Shader::timeNameId,
267 Shader::eyePositionNameId,
268 Shader::skinningPaletteNameId,
269 };
270
271 static const std::vector<int> lightUniformNameIds = getLightUniformNameIds();
272
273 for (size_t i = 0, m = uniformsDescription.size(); i < m; i++) {
274 m_uniformsNames[i] = m_uniforms[i].m_name;
275 const int nameId = StringToInt::lookupId(str: m_uniformsNames[i]);
276 m_uniforms[i].m_nameId = nameId;
277
278 // Is the uniform a Qt3D "Standard" uniform, a light uniform or a user defined one?
279 if (Qt3DCore::contains(destination: standardUniformNameIds, element: nameId))
280 m_standardUniformNamesIds.push_back(x: nameId);
281 else if (Qt3DCore::contains(destination: lightUniformNameIds, element: nameId))
282 m_lightUniformsNamesIds.push_back(x: nameId);
283 else
284 m_uniformsNamesIds.push_back(x: nameId);
285
286 if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block
287 qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex;
288 activeUniformsInDefaultBlock.insert(key: uniformsDescription[i].m_name, value: uniformsDescription[i]);
289 }
290 }
291 m_uniformBlockIndexToShaderUniforms.insert(key: -1, value: activeUniformsInDefaultBlock);
292
293 m_parameterPackSize += int(m_standardUniformNamesIds.size()) + int(m_lightUniformsNamesIds.size()) + int(m_uniformsNamesIds.size());
294 m_hasActiveVariables |= (m_parameterPackSize > 0);
295
296 // Sort by ascending order to make contains check faster
297 std::sort(first: m_uniformsNamesIds.begin(), last: m_uniformsNamesIds.end());
298 std::sort(first: m_lightUniformsNamesIds.begin(), last: m_lightUniformsNamesIds.end());
299 std::sort(first: m_standardUniformNamesIds.begin(), last: m_standardUniformNamesIds.end());
300 std::sort(first: m_uniforms.begin(), last: m_uniforms.end(),
301 comp: [] (const ShaderUniform &a, const ShaderUniform &b) {
302 return a.m_nameId < b.m_nameId;
303 });
304}
305
306void GLShader::initializeAttributes(const std::vector<ShaderAttribute> &attributesDescription)
307{
308 m_attributes = attributesDescription;
309 m_attributesNames.resize(new_size: attributesDescription.size());
310 m_attributeNamesIds.resize(new_size: attributesDescription.size());
311 for (int i = 0, m = int(attributesDescription.size()); i < m; i++) {
312 m_attributesNames[i] = attributesDescription[i].m_name;
313 m_attributes[i].m_nameId = StringToInt::lookupId(str: m_attributesNames[i]);
314 m_attributeNamesIds[i] = m_attributes[i].m_nameId;
315 qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
316 }
317 m_hasActiveVariables |= (m_attributeNamesIds.size() > 0);
318}
319
320void GLShader::initializeUniformBlocks(const std::vector<ShaderUniformBlock> &uniformBlockDescription)
321{
322 m_uniformBlocks = uniformBlockDescription;
323 m_uniformBlockNames.resize(new_size: uniformBlockDescription.size());
324 m_uniformBlockNamesIds.resize(new_size: uniformBlockDescription.size());
325 for (int i = 0, m = int(uniformBlockDescription.size()); i < m; ++i) {
326 m_uniformBlockNames[i] = m_uniformBlocks[i].m_name;
327 m_uniformBlockNamesIds[i] = StringToInt::lookupId(str: m_uniformBlockNames[i]);
328 m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i];
329 qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}";
330
331 // Find all active uniforms for the shader block
332 std::vector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
333 const std::vector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
334
335 std::vector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
336 const std::vector<QString>::const_iterator uniformNamesEnd = m_uniformsNames.cend();
337
338 QHash<QString, ShaderUniform> activeUniformsInBlock;
339
340 while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) {
341 if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) {
342 QString uniformName = *uniformNamesIt;
343 if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(s: m_uniformBlockNames[i]))
344 uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt;
345 activeUniformsInBlock.insert(key: uniformName, value: *uniformsIt);
346 qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex;
347 }
348 ++uniformsIt;
349 ++uniformNamesIt;
350 }
351 m_uniformBlockIndexToShaderUniforms.insert(key: uniformBlockDescription[i].m_index, value: activeUniformsInBlock);
352 }
353
354 m_parameterPackSize += int(m_uniformsNamesIds.size());
355 m_hasActiveVariables |= (m_parameterPackSize > 0);
356
357 // Sort by ascending order to make contains check faster
358 std::sort(first: m_uniformBlockNamesIds.begin(), last: m_uniformBlockNamesIds.end());
359}
360
361void GLShader::initializeShaderStorageBlocks(const std::vector<ShaderStorageBlock> &shaderStorageBlockDescription)
362{
363 m_shaderStorageBlocks = shaderStorageBlockDescription;
364 m_shaderStorageBlockNames.resize(new_size: shaderStorageBlockDescription.size());
365 m_shaderStorageBlockNamesIds.resize(new_size: shaderStorageBlockDescription.size());
366
367 for (int i = 0, m = int(shaderStorageBlockDescription.size()); i < m; ++i) {
368 m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name;
369 m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(str: m_shaderStorageBlockNames[i]);
370 m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
371 qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
372 }
373
374 m_parameterPackSize += int(m_shaderStorageBlockNamesIds.size());
375 m_hasActiveVariables |= (m_parameterPackSize > 0);
376
377 // Sort by ascending order to make contains check faster
378 std::sort(first: m_shaderStorageBlockNamesIds.begin(), last: m_shaderStorageBlockNamesIds.end());
379}
380
381} // OpenGL
382
383} // Render
384
385} // Qt3DRender
386
387QT_END_NAMESPACE
388

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