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 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | namespace Qt3DRender { |
15 | |
16 | namespace Render { |
17 | |
18 | namespace OpenGL { |
19 | |
20 | namespace { |
21 | |
22 | std::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 | |
52 | template<typename Vector> |
53 | bool fastContains(const Vector &v, int value) |
54 | { |
55 | return std::binary_search(v.cbegin(), v.cend(), value); |
56 | } |
57 | |
58 | } |
59 | |
60 | GLShader::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 | |
69 | GLShader::~GLShader() |
70 | { |
71 | if (m_contextConnection) |
72 | QObject::disconnect(m_contextConnection); |
73 | } |
74 | |
75 | void 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 | |
87 | GraphicsContext *GLShader::graphicsContext() |
88 | { |
89 | QMutexLocker lock(&m_mutex); |
90 | return m_graphicsContext; |
91 | } |
92 | |
93 | |
94 | const std::vector<QString> &GLShader::uniformsNames() const |
95 | { |
96 | return m_uniformsNames; |
97 | } |
98 | |
99 | const std::vector<QString> &GLShader::attributesNames() const |
100 | { |
101 | return m_attributesNames; |
102 | } |
103 | |
104 | const std::vector<QString> &GLShader::uniformBlockNames() const |
105 | { |
106 | return m_uniformBlockNames; |
107 | } |
108 | |
109 | const std::vector<QString> &GLShader::storageBlockNames() const |
110 | { |
111 | return m_shaderStorageBlockNames; |
112 | } |
113 | |
114 | const std::vector<QByteArray> &GLShader::shaderCode() const |
115 | { |
116 | return m_shaderCode; |
117 | } |
118 | |
119 | QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const |
120 | { |
121 | return m_uniformBlockIndexToShaderUniforms.value(key: blockIndex); |
122 | } |
123 | |
124 | ShaderUniformBlock 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 | |
134 | ShaderUniformBlock 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 | |
144 | ShaderUniformBlock 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 | |
154 | ShaderStorageBlock 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 | |
163 | ShaderStorageBlock 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 | |
172 | ShaderStorageBlock 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 | |
181 | GLShader::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 | |
192 | bool GLShader::hasUniform(int nameId) const noexcept |
193 | { |
194 | return Qt3DCore::contains(destination: m_uniformsNamesIds, element: nameId); |
195 | } |
196 | |
197 | void 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 | |
222 | void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs) |
223 | { |
224 | { |
225 | QMutexLocker lock(&m_mutex); |
226 | m_fragOutputs = fragOutputs; |
227 | } |
228 | } |
229 | |
230 | const QHash<QString, int> GLShader::fragOutputs() const |
231 | { |
232 | QMutexLocker lock(&m_mutex); |
233 | return m_fragOutputs; |
234 | } |
235 | |
236 | void 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 | |
306 | void 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 | |
320 | void 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 | |
361 | void 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 | |
387 | QT_END_NAMESPACE |
388 | |