1 | // Copyright (C) 2014 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 "qshaderprogram.h" |
5 | #include "qshaderprogram_p.h" |
6 | #include <Qt3DCore/private/qurlhelper_p.h> |
7 | #include <QDebug> |
8 | #include <QFile> |
9 | #include <QFileInfo> |
10 | #include <QUrl> |
11 | #include <QRegularExpression> |
12 | |
13 | /*! |
14 | \class Qt3DRender::QShaderProgram |
15 | \inmodule Qt3DRender |
16 | \brief Encapsulates a Shader Program. |
17 | \inherits Qt3DCore::QNode |
18 | \since 5.5 |
19 | |
20 | A shader program consists of several different shaders, such as vertex and fragment shaders. |
21 | |
22 | Qt3D will automatically populate a set of default uniforms if they are |
23 | encountered during the shader instrospection phase. |
24 | |
25 | \table |
26 | \header |
27 | \li Default Uniform |
28 | \li Associated Qt3D Parameter name |
29 | \li GLSL declaration |
30 | |
31 | \row |
32 | \li ModelMatrix |
33 | \li modelMatrix |
34 | \li uniform mat4 modelMatrix; |
35 | |
36 | \row |
37 | \li ViewMatrix |
38 | \li viewMatrix |
39 | \li uniform mat4 viewMatrix; |
40 | |
41 | \row |
42 | \li ProjectionMatrix |
43 | \li projectionMatrix |
44 | \li uniform mat4 projectionMatrix; |
45 | |
46 | \row |
47 | \li ModelViewMatrix |
48 | \li modelView |
49 | \li uniform mat4 modelView; |
50 | |
51 | \row |
52 | \li ViewProjectionMatrix |
53 | \li viewProjectionMatrix |
54 | \li uniform mat4 viewProjectionMatrix; |
55 | |
56 | \row |
57 | \li ModelViewProjectionMatrix |
58 | \li modelViewProjection \br mvp |
59 | \li uniform mat4 modelViewProjection; \br uniform mat4 mvp; |
60 | |
61 | \row |
62 | \li InverseModelMatrix |
63 | \li inverseModelMatrix |
64 | \li uniform mat4 inverseModelMatrix; |
65 | |
66 | \row |
67 | \li InverseViewMatrix |
68 | \li inverseViewMatrix |
69 | \li uniform mat4 inverseViewMatrix; |
70 | |
71 | \row |
72 | \li InverseProjectionMatrix |
73 | \li inverseProjectionMatrix |
74 | \li uniform mat4 inverseProjectionMatrix; |
75 | |
76 | \row |
77 | \li InverseModelViewMatrix |
78 | \li inverseModelView |
79 | \li uniform mat4 inverseModelView; |
80 | |
81 | \row |
82 | \li InverseViewProjectionMatrix |
83 | \li inverseViewProjectionMatrix |
84 | \li uniform mat4 inverseViewProjectionMatrix; |
85 | |
86 | \row |
87 | \li InverseModelViewProjectionMatrix |
88 | \li inverseModelViewProjection |
89 | \li uniform mat4 inverseModelViewProjection; |
90 | |
91 | \row |
92 | \li ModelNormalMatrix |
93 | \li modelNormalMatrix |
94 | \li uniform mat3 modelNormalMatrix; |
95 | |
96 | \row |
97 | \li ModelViewNormalMatrix |
98 | \li modelViewNormal |
99 | \li uniform mat3 modelViewNormal; |
100 | |
101 | \row |
102 | \li ViewportMatrix |
103 | \li viewportMatrix |
104 | \li uniform mat4 viewportMatrix; |
105 | |
106 | \row |
107 | \li InverseViewportMatrix |
108 | \li inverseViewportMatrix |
109 | \li uniform mat4 inverseViewportMatrix; |
110 | |
111 | \row |
112 | \li AspectRatio \br (surface width / surface height) |
113 | \li aspectRatio |
114 | \li uniform float aspectRatio; |
115 | |
116 | \row |
117 | \li Exposure |
118 | \li exposure |
119 | \li uniform float exposure; |
120 | |
121 | \row |
122 | \li Gamma |
123 | \li gamma |
124 | \li uniform float gamma; |
125 | |
126 | \row |
127 | \li Time \br (in nano seconds) |
128 | \li time |
129 | \li uniform float time; |
130 | |
131 | \row |
132 | \li EyePosition |
133 | \li eyePosition |
134 | \li uniform vec3 eyePosition; |
135 | |
136 | \row |
137 | \li SkinningPalette |
138 | \li skinningPalette[0] |
139 | \li const int maxJoints = 100; \br uniform mat4 skinningPalette[maxJoints]; |
140 | |
141 | \endtable |
142 | |
143 | \section1 RHI Support |
144 | |
145 | When writing GLSL 450 shader code to use with Qt 3D's RHI backend, |
146 | the default uniforms will be provided as 2 uniform buffer objects. |
147 | |
148 | The binding locations for these is set to bindings 0 for RenderView |
149 | uniforms and 1 for Command uniforms. |
150 | |
151 | \badcode |
152 | #version 450 core |
153 | |
154 | layout(location = 0) in vec3 vertexPosition; |
155 | |
156 | layout(std140, binding = 0) uniform qt3d_render_view_uniforms { |
157 | mat4 viewMatrix; |
158 | mat4 projectionMatrix; |
159 | mat4 uncorrectedProjectionMatrix; |
160 | mat4 clipCorrectionMatrix; |
161 | mat4 viewProjectionMatrix; |
162 | mat4 inverseViewMatrix; |
163 | mat4 inverseProjectionMatrix; |
164 | mat4 inverseViewProjectionMatrix; |
165 | mat4 viewportMatrix; |
166 | mat4 inverseViewportMatrix; |
167 | vec4 textureTransformMatrix; |
168 | vec3 eyePosition; |
169 | float aspectRatio; |
170 | float gamma; |
171 | float exposure; |
172 | float time; |
173 | float yUpInNDC; |
174 | float yUpInFBO; |
175 | }; |
176 | |
177 | layout(std140, binding = 1) uniform qt3d_command_uniforms { |
178 | mat4 modelMatrix; |
179 | mat4 inverseModelMatrix; |
180 | mat4 modelViewMatrix; |
181 | mat3 modelNormalMatrix; |
182 | mat4 inverseModelViewMatrix; |
183 | mat4 modelViewProjection; |
184 | mat4 inverseModelViewProjectionMatrix; |
185 | }; |
186 | |
187 | void main() |
188 | { |
189 | gl_Position = (projectionMatrix * viewMatrix * modelMatrix * vertexPosition); |
190 | } |
191 | \endcode |
192 | |
193 | For user defined uniform buffer object, use binding starting at 2 or auto |
194 | to let Qt 3D work out the binding automatically. Make sure to remain |
195 | consistent between the different shader stages. |
196 | |
197 | |
198 | \badcode |
199 | #version 450 core |
200 | |
201 | layout(std140, binding = auto) uniform my_uniforms { |
202 | vec4 myColor; |
203 | }; |
204 | |
205 | layout(location=0) out vec4 fragColor; |
206 | |
207 | void main() |
208 | { |
209 | fragColor = myColor; |
210 | } |
211 | \endcode |
212 | |
213 | There is no change involved when it comes to feeding values to uniforms. |
214 | |
215 | For the above example, setting myColor could be done with: |
216 | |
217 | \badcode |
218 | QParameter *parameter = new QParameter(); |
219 | parameter->setName("myColor"); |
220 | parameter->setValue(QVariant::fromValue(QColor(Qt::blue))); |
221 | \endcode |
222 | |
223 | Textures still have to be defined as standalone uniforms. |
224 | |
225 | \badcode |
226 | #version 450 core |
227 | |
228 | layout(binding=0) uniform sampler2D source; |
229 | |
230 | layout(location=0) out vec4 fragColor; |
231 | |
232 | void main() |
233 | { |
234 | fragColor = texture(source, vec2(0.5, 0.5)); |
235 | } |
236 | \endcode |
237 | */ |
238 | |
239 | /*! |
240 | \qmltype ShaderProgram |
241 | \instantiates Qt3DRender::QShaderProgram |
242 | \inqmlmodule Qt3D.Render |
243 | \brief Encapsulates a Shader Program. |
244 | \since 5.5 |
245 | |
246 | ShaderProgram class encapsulates a shader program. A shader program consists of several |
247 | different shaders, such as vertex and fragment shaders. |
248 | |
249 | Qt3D will automatically populate a set of default uniforms if they are |
250 | encountered during the shader instrospection phase. |
251 | |
252 | \table |
253 | \header |
254 | \li {1, 1} Default Uniform |
255 | \li {2, 1} Associated Qt3D Parameter name |
256 | \li {3, 1} GLSL declaration |
257 | |
258 | \row |
259 | \li {1, 1} ModelMatrix |
260 | \li {2, 1} modelMatrix |
261 | \li {3, 1} uniform mat4 modelMatrix; |
262 | |
263 | \row |
264 | \li {1, 1} ViewMatrix |
265 | \li {2, 1} viewMatrix |
266 | \li {3, 1} uniform mat4 viewMatrix; |
267 | |
268 | \row |
269 | \li {1, 1} ProjectionMatrix |
270 | \li {2, 1} projectionMatrix |
271 | \li {3, 1} uniform mat4 projectionMatrix; |
272 | |
273 | \row |
274 | \li {1, 1} ModelViewMatrix |
275 | \li {2, 1} modelView |
276 | \li {3, 1} uniform mat4 modelView; |
277 | |
278 | \row |
279 | \li {1, 1} ViewProjectionMatrix |
280 | \li {2, 1} viewProjectionMatrix |
281 | \li {3, 1} uniform mat4 viewProjectionMatrix; |
282 | |
283 | \row |
284 | \li {1, 1} ModelViewProjectionMatrix |
285 | \li {2, 1} modelViewProjection \br mvp |
286 | \li {3, 1} uniform mat4 modelViewProjection; \br uniform mat4 mvp; |
287 | |
288 | \row |
289 | \li {1, 1} InverseModelMatrix |
290 | \li {2, 1} inverseModelMatrix |
291 | \li {3, 1} uniform mat4 inverseModelMatrix; |
292 | |
293 | \row |
294 | \li {1, 1} InverseViewMatrix |
295 | \li {2, 1} inverseViewMatrix |
296 | \li {3, 1} uniform mat4 inverseViewMatrix; |
297 | |
298 | \row |
299 | \li {1, 1} InverseProjectionMatrix |
300 | \li {2, 1} inverseProjectionMatrix |
301 | \li {3, 1} uniform mat4 inverseProjectionMatrix; |
302 | |
303 | \row |
304 | \li {1, 1} InverseModelViewMatrix |
305 | \li {2, 1} inverseModelView |
306 | \li {3, 1} uniform mat4 inverseModelView; |
307 | |
308 | \row |
309 | \li {1, 1} InverseViewProjectionMatrix |
310 | \li {2, 1} inverseViewProjectionMatrix |
311 | \li {3, 1} uniform mat4 inverseViewProjectionMatrix; |
312 | |
313 | \row |
314 | \li {1, 1} InverseModelViewProjectionMatrix |
315 | \li {2, 1} inverseModelViewProjection |
316 | \li {3, 1} uniform mat4 inverseModelViewProjection; |
317 | |
318 | \row |
319 | \li {1, 1} ModelNormalMatrix |
320 | \li {2, 1} modelNormalMatrix |
321 | \li {3, 1} uniform mat3 modelNormalMatrix; |
322 | |
323 | \row |
324 | \li {1, 1} ModelViewNormalMatrix |
325 | \li {2, 1} modelViewNormal |
326 | \li {3, 1} uniform mat3 modelViewNormal; |
327 | |
328 | \row |
329 | \li {1, 1} ViewportMatrix |
330 | \li {2, 1} viewportMatrix |
331 | \li {3, 1} uniform mat4 viewportMatrix; |
332 | |
333 | \row |
334 | \li {1, 1} InverseViewportMatrix |
335 | \li {2, 1} inverseViewportMatrix |
336 | \li {3, 1} uniform mat4 inverseViewportMatrix; |
337 | |
338 | \row |
339 | \li {1, 1} AspectRatio \br (surface width / surface height) |
340 | \li {2, 1} aspectRatio |
341 | \li {3, 1} uniform float aspectRatio; |
342 | |
343 | \row |
344 | \li {1, 1} Exposure |
345 | \li {2, 1} exposure |
346 | \li {3, 1} uniform float exposure; |
347 | |
348 | \row |
349 | \li {1, 1} Gamma |
350 | \li {2, 1} gamma |
351 | \li {3, 1} uniform float gamma; |
352 | |
353 | \row |
354 | \li {1, 1} Time \br (in nano seconds) |
355 | \li {2, 1} time |
356 | \li {3, 1} uniform float time; |
357 | |
358 | \row |
359 | \li {1, 1} EyePosition |
360 | \li {2, 1} eyePosition |
361 | \li {3, 1} uniform vec3 eyePosition; |
362 | |
363 | \row |
364 | \li {1, 1} SkinningPalette |
365 | \li {2, 1} skinningPalette[0] |
366 | \li {3, 1} const int maxJoints = 100; \br uniform mat4 skinningPalette[maxJoints]; |
367 | |
368 | \endtable |
369 | |
370 | \section1 RHI Support |
371 | |
372 | When writing GLSL 450 shader code to use with Qt 3D's RHI backend, |
373 | the default uniforms will be provided as 2 uniform buffer objects. |
374 | |
375 | The binding locations for these is set to bindings 0 for RenderView |
376 | uniforms and 1 for Command uniforms. |
377 | |
378 | \badcode |
379 | #version 450 core |
380 | |
381 | layout(location = 0) in vec3 vertexPosition; |
382 | |
383 | layout(std140, binding = 0) uniform qt3d_render_view_uniforms { |
384 | mat4 viewMatrix; |
385 | mat4 projectionMatrix; |
386 | mat4 uncorrectedProjectionMatrix; |
387 | mat4 clipCorrectionMatrix; |
388 | mat4 viewProjectionMatrix; |
389 | mat4 inverseViewMatrix; |
390 | mat4 inverseProjectionMatrix; |
391 | mat4 inverseViewProjectionMatrix; |
392 | mat4 viewportMatrix; |
393 | mat4 inverseViewportMatrix; |
394 | vec4 textureTransformMatrix; |
395 | vec3 eyePosition; |
396 | float aspectRatio; |
397 | float gamma; |
398 | float exposure; |
399 | float time; |
400 | float yUpInNDC; |
401 | float yUpInFBO; |
402 | }; |
403 | |
404 | layout(std140, binding = 1) uniform qt3d_command_uniforms { |
405 | mat4 modelMatrix; |
406 | mat4 inverseModelMatrix; |
407 | mat4 modelViewMatrix; |
408 | mat3 modelNormalMatrix; |
409 | mat4 inverseModelViewMatrix; |
410 | mat4 modelViewProjection; |
411 | mat4 inverseModelViewProjectionMatrix; |
412 | }; |
413 | |
414 | void main() |
415 | { |
416 | gl_Position = (projectionMatrix * viewMatrix * modelMatrix * vertexPosition); |
417 | } |
418 | \endcode |
419 | |
420 | For user defined uniform buffer object, use binding starting at 2 or auto |
421 | to let Qt 3D work out the binding automatically. Make sure to remain |
422 | consistent between the different shader stages. |
423 | |
424 | |
425 | \badcode |
426 | #version 450 core |
427 | |
428 | layout(std140, binding = auto) uniform my_uniforms { |
429 | vec4 myColor; |
430 | }; |
431 | |
432 | layout(location=0) out vec4 fragColor; |
433 | |
434 | void main() |
435 | { |
436 | fragColor = myColor; |
437 | } |
438 | \endcode |
439 | |
440 | There is no change involved when it comes to feeding values to uniforms. |
441 | |
442 | For the above example, setting myColor could be done with: |
443 | |
444 | \badcode |
445 | Parameter { name: "myColor"; value: "blue" } |
446 | \endcode |
447 | |
448 | Textures still have to be defined as standalone uniforms. |
449 | |
450 | \badcode |
451 | #version 450 core |
452 | |
453 | layout(binding=0) uniform sampler2D source; |
454 | |
455 | layout(location=0) out vec4 fragColor; |
456 | |
457 | void main() |
458 | { |
459 | fragColor = texture(source, vec2(0.5, 0.5)); |
460 | } |
461 | \endcode |
462 | */ |
463 | |
464 | /*! |
465 | \enum QShaderProgram::ShaderType |
466 | |
467 | This enum identifies the type of shader used. |
468 | |
469 | \value Vertex Vertex shader |
470 | \value Fragment Fragment shader |
471 | \value TessellationControl Tesselation control shader |
472 | \value TessellationEvaluation Tesselation evaluation shader |
473 | \value Geometry Geometry shader |
474 | \value Compute Compute shader |
475 | */ |
476 | |
477 | /*! |
478 | \enum QShaderProgram::Status |
479 | |
480 | This enum identifies the status of shader used. |
481 | |
482 | \value NotReady The shader hasn't been compiled and linked yet |
483 | \value Ready The shader was successfully compiled |
484 | \value Error An error occurred while compiling the shader |
485 | */ |
486 | |
487 | /*! |
488 | \enum QShaderProgram::Format |
489 | |
490 | This enum identifies the format of the shader code used. |
491 | |
492 | \value GLSL OpenGL |
493 | \value SPIRV Vulkan, OpenGL 5 |
494 | |
495 | \since 5.15 |
496 | */ |
497 | |
498 | QT_BEGIN_NAMESPACE |
499 | |
500 | namespace Qt3DRender { |
501 | |
502 | QShaderProgramPrivate::QShaderProgramPrivate() |
503 | : QNodePrivate() |
504 | , m_status(QShaderProgram::NotReady) |
505 | , m_format(QShaderProgram::GLSL) |
506 | { |
507 | } |
508 | |
509 | void QShaderProgramPrivate::setLog(const QString &log) |
510 | { |
511 | Q_Q(QShaderProgram); |
512 | if (log != m_log) { |
513 | m_log = log; |
514 | const bool blocked = q->blockNotifications(block: true); |
515 | emit q->logChanged(log: m_log); |
516 | q->blockNotifications(block: blocked); |
517 | } |
518 | } |
519 | |
520 | void QShaderProgramPrivate::setStatus(QShaderProgram::Status status) |
521 | { |
522 | Q_Q(QShaderProgram); |
523 | if (status != m_status) { |
524 | m_status = status; |
525 | const bool blocked = q->blockNotifications(block: true); |
526 | emit q->statusChanged(status: m_status); |
527 | q->blockNotifications(block: blocked); |
528 | } |
529 | } |
530 | |
531 | QShaderProgram::QShaderProgram(QNode *parent) |
532 | : QNode(*new QShaderProgramPrivate, parent) |
533 | { |
534 | } |
535 | |
536 | QShaderProgram::~QShaderProgram() |
537 | { |
538 | } |
539 | |
540 | /*! \internal */ |
541 | QShaderProgram::QShaderProgram(QShaderProgramPrivate &dd, QNode *parent) |
542 | : QNode(dd, parent) |
543 | { |
544 | } |
545 | |
546 | /*! |
547 | \qmlproperty string ShaderProgram::vertexShaderCode |
548 | |
549 | Holds the vertex shader code used by this shader program. |
550 | */ |
551 | /*! |
552 | \property QShaderProgram::vertexShaderCode |
553 | |
554 | Holds the vertex shader code used by this shader program. |
555 | */ |
556 | void QShaderProgram::setVertexShaderCode(const QByteArray &vertexShaderCode) |
557 | { |
558 | Q_D(QShaderProgram); |
559 | if (vertexShaderCode != d->m_vertexShaderCode) { |
560 | d->m_vertexShaderCode = vertexShaderCode; |
561 | emit vertexShaderCodeChanged(vertexShaderCode); |
562 | } |
563 | } |
564 | |
565 | QByteArray QShaderProgram::vertexShaderCode() const |
566 | { |
567 | Q_D(const QShaderProgram); |
568 | return d->m_vertexShaderCode; |
569 | } |
570 | |
571 | /*! |
572 | \qmlproperty string ShaderProgram::tessellationControlShaderCode |
573 | |
574 | Holds the tesselation control shader code used by this shader program. |
575 | */ |
576 | /*! |
577 | \property QShaderProgram::tessellationControlShaderCode |
578 | |
579 | Holds the tesselation control shader code used by this shader program. |
580 | */ |
581 | void QShaderProgram::setTessellationControlShaderCode(const QByteArray &tessellationControlShaderCode) |
582 | { |
583 | Q_D(QShaderProgram); |
584 | if (tessellationControlShaderCode != d->m_tessControlShaderCode) { |
585 | d->m_tessControlShaderCode = tessellationControlShaderCode; |
586 | emit tessellationControlShaderCodeChanged(tessellationControlShaderCode); |
587 | } |
588 | } |
589 | |
590 | QByteArray QShaderProgram::tessellationControlShaderCode() const |
591 | { |
592 | Q_D(const QShaderProgram); |
593 | return d->m_tessControlShaderCode; |
594 | } |
595 | |
596 | /*! |
597 | \qmlproperty string ShaderProgram::tessellationEvaluationShaderCode |
598 | |
599 | Holds the tesselation evaluation shader code used by this shader program. |
600 | */ |
601 | /*! |
602 | \property QShaderProgram::tessellationEvaluationShaderCode |
603 | |
604 | Holds the tesselation evaluation shader code used by this shader program. |
605 | */ |
606 | void QShaderProgram::setTessellationEvaluationShaderCode(const QByteArray &tessellationEvaluationShaderCode) |
607 | { |
608 | Q_D(QShaderProgram); |
609 | if (tessellationEvaluationShaderCode != d->m_tessEvalShaderCode) { |
610 | d->m_tessEvalShaderCode = tessellationEvaluationShaderCode; |
611 | emit tessellationEvaluationShaderCodeChanged(tessellationEvaluationShaderCode); |
612 | } |
613 | } |
614 | |
615 | QByteArray QShaderProgram::tessellationEvaluationShaderCode() const |
616 | { |
617 | Q_D(const QShaderProgram); |
618 | return d->m_tessEvalShaderCode; |
619 | } |
620 | |
621 | /*! |
622 | \qmlproperty string ShaderProgram::geometryShaderCode |
623 | |
624 | Holds the geometry shader code used by this shader program. |
625 | */ |
626 | /*! |
627 | \property QShaderProgram::geometryShaderCode |
628 | |
629 | Holds the geometry shader code used by this shader program. |
630 | */ |
631 | void QShaderProgram::setGeometryShaderCode(const QByteArray &geometryShaderCode) |
632 | { |
633 | Q_D(QShaderProgram); |
634 | if (geometryShaderCode != d->m_geometryShaderCode) { |
635 | d->m_geometryShaderCode = geometryShaderCode; |
636 | emit geometryShaderCodeChanged(geometryShaderCode); |
637 | } |
638 | } |
639 | |
640 | QByteArray QShaderProgram::geometryShaderCode() const |
641 | { |
642 | Q_D(const QShaderProgram); |
643 | return d->m_geometryShaderCode; |
644 | } |
645 | |
646 | /*! |
647 | \qmlproperty string ShaderProgram::fragmentShaderCode |
648 | |
649 | Holds the fragment shader code used by this shader program. |
650 | */ |
651 | /*! |
652 | \property QShaderProgram::fragmentShaderCode |
653 | |
654 | Holds the fragment shader code used by this shader program. |
655 | */ |
656 | void QShaderProgram::setFragmentShaderCode(const QByteArray &fragmentShaderCode) |
657 | { |
658 | Q_D(QShaderProgram); |
659 | if (fragmentShaderCode != d->m_fragmentShaderCode) { |
660 | d->m_fragmentShaderCode = fragmentShaderCode; |
661 | emit fragmentShaderCodeChanged(fragmentShaderCode); |
662 | } |
663 | } |
664 | |
665 | QByteArray QShaderProgram::fragmentShaderCode() const |
666 | { |
667 | Q_D(const QShaderProgram); |
668 | return d->m_fragmentShaderCode; |
669 | } |
670 | |
671 | /*! |
672 | \qmlproperty string ShaderProgram::computeShaderCode |
673 | |
674 | Holds the compute shader code used by this shader program. |
675 | */ |
676 | /*! |
677 | \property QShaderProgram::computeShaderCode |
678 | |
679 | Holds the compute shader code used by this shader program. |
680 | */ |
681 | void QShaderProgram::setComputeShaderCode(const QByteArray &computeShaderCode) |
682 | { |
683 | Q_D(QShaderProgram); |
684 | if (computeShaderCode != d->m_computeShaderCode) { |
685 | d->m_computeShaderCode = computeShaderCode; |
686 | emit computeShaderCodeChanged(computeShaderCode); |
687 | } |
688 | } |
689 | |
690 | QByteArray QShaderProgram::computeShaderCode() const |
691 | { |
692 | Q_D(const QShaderProgram); |
693 | return d->m_computeShaderCode; |
694 | } |
695 | |
696 | |
697 | /*! |
698 | Sets the shader code for \a type of shader to the \a shaderCode. |
699 | */ |
700 | void QShaderProgram::setShaderCode(ShaderType type, const QByteArray &shaderCode) |
701 | { |
702 | switch (type) { |
703 | case Vertex: |
704 | setVertexShaderCode(shaderCode); |
705 | break; |
706 | case TessellationControl: |
707 | setTessellationControlShaderCode(shaderCode); |
708 | break; |
709 | case TessellationEvaluation: |
710 | setTessellationEvaluationShaderCode(shaderCode); |
711 | break; |
712 | case Geometry: |
713 | setGeometryShaderCode(shaderCode); |
714 | break; |
715 | case Fragment: |
716 | setFragmentShaderCode(shaderCode); |
717 | break; |
718 | case Compute: |
719 | setComputeShaderCode(shaderCode); |
720 | break; |
721 | default: |
722 | Q_UNREACHABLE(); |
723 | } |
724 | } |
725 | |
726 | /*! |
727 | Returns the shader code for \a type. |
728 | */ |
729 | QByteArray QShaderProgram::shaderCode(ShaderType type) const |
730 | { |
731 | Q_D(const QShaderProgram); |
732 | switch (type) { |
733 | case Vertex: |
734 | return d->m_vertexShaderCode; |
735 | case TessellationControl: |
736 | return d->m_tessControlShaderCode; |
737 | case TessellationEvaluation: |
738 | return d->m_tessEvalShaderCode; |
739 | case Geometry: |
740 | return d->m_geometryShaderCode; |
741 | case Fragment: |
742 | return d->m_fragmentShaderCode; |
743 | case Compute: |
744 | return d->m_computeShaderCode; |
745 | default: |
746 | Q_UNREACHABLE(); |
747 | } |
748 | } |
749 | |
750 | /*! |
751 | \qmlproperty string ShaderProgram::log |
752 | |
753 | Holds the log of the current shader program. This is useful to diagnose a |
754 | compilation failure of the shader program. |
755 | */ |
756 | /*! |
757 | \property QShaderProgram::log |
758 | |
759 | Holds the log of the current shader program. This is useful to diagnose a |
760 | compilation failure of the shader program. |
761 | */ |
762 | QString QShaderProgram::log() const |
763 | { |
764 | Q_D(const QShaderProgram); |
765 | return d->m_log; |
766 | } |
767 | |
768 | /*! |
769 | \qmlproperty enumeration ShaderProgram::status |
770 | |
771 | Holds the status of the current shader program. |
772 | */ |
773 | /*! |
774 | \property QShaderProgram::status |
775 | |
776 | Holds the status of the current shader program. |
777 | */ |
778 | /*! |
779 | Returns the status of the current shader program. |
780 | */ |
781 | QShaderProgram::Status QShaderProgram::status() const |
782 | { |
783 | Q_D(const QShaderProgram); |
784 | return d->m_status; |
785 | } |
786 | |
787 | void QShaderProgram::setFormat(QShaderProgram::Format format) |
788 | { |
789 | Q_D(QShaderProgram); |
790 | if (format != d->m_format) { |
791 | d->m_format = format; |
792 | emit formatChanged(format); |
793 | } |
794 | } |
795 | |
796 | /*! |
797 | \qmlproperty enumeration ShaderProgram::format |
798 | \since 5.15 |
799 | |
800 | Holds the format of the code provided on the ShaderProgram. |
801 | The default is ShaderProgram.GLSL |
802 | */ |
803 | /*! |
804 | \property QShaderProgram::format |
805 | \since 5.15 |
806 | |
807 | Holds the format of the code provided on the ShaderProgram. |
808 | The default is ShaderProgram.GLSL |
809 | */ |
810 | QShaderProgram::Format QShaderProgram::format() const |
811 | { |
812 | Q_D(const QShaderProgram); |
813 | return d->m_format; |
814 | } |
815 | |
816 | QByteArray QShaderProgramPrivate::deincludify(const QString &filePath) |
817 | { |
818 | QFile f(filePath); |
819 | if (!f.open(flags: QIODevice::ReadOnly | QIODevice::Text)) { |
820 | qWarning() << "Could not read shader source file:" << f.fileName(); |
821 | return QByteArray(); |
822 | } |
823 | |
824 | QByteArray contents = f.readAll(); |
825 | return deincludify(contents, filePath); |
826 | } |
827 | |
828 | QByteArray QShaderProgramPrivate::resolveAutoBindingIndices(const QByteArray &content, |
829 | int ¤tBinding, |
830 | int ¤tInputLocation, |
831 | int ¤tOutputLocation) |
832 | { |
833 | QString shaderCode = QString::fromUtf8(ba: content); |
834 | |
835 | // This lambda will replace all occurrences of a string (e.g. "binding = auto") by another, |
836 | // with the incremented int passed as argument (e.g. "binding = 1", "binding = 2" ...) |
837 | const auto replaceAndIncrement = [&](const QRegularExpression ®exp, |
838 | int &variable, |
839 | const QString &replacement) noexcept { |
840 | qsizetype matchStart = 0; |
841 | do { |
842 | matchStart = shaderCode.indexOf(re: regexp, from: matchStart); |
843 | if (matchStart != -1) { |
844 | const auto match = regexp.matchView(subjectView: QStringView{shaderCode}.mid(pos: matchStart)); |
845 | const auto length = match.capturedLength(nth: 0); |
846 | shaderCode.replace(i: matchStart, len: length, after: replacement.arg(a: variable++)); |
847 | } |
848 | } while (matchStart != -1); |
849 | }; |
850 | |
851 | // 1. Handle uniforms |
852 | { |
853 | thread_local const QRegularExpression bindings( |
854 | QStringLiteral("binding\\s*=\\s*auto" )); |
855 | |
856 | replaceAndIncrement(bindings, currentBinding, QStringLiteral("binding = %1" )); |
857 | } |
858 | |
859 | // 2. Handle inputs |
860 | { |
861 | thread_local const QRegularExpression inLocations( |
862 | QStringLiteral("location\\s*=\\s*auto\\s*\\)\\s*in\\s+" )); |
863 | |
864 | replaceAndIncrement(inLocations, currentInputLocation, |
865 | QStringLiteral("location = %1) in " )); |
866 | } |
867 | |
868 | // 3. Handle outputs |
869 | { |
870 | thread_local const QRegularExpression outLocations( |
871 | QStringLiteral("location\\s*=\\s*auto\\s*\\)\\s*out\\s+" )); |
872 | |
873 | replaceAndIncrement(outLocations, currentOutputLocation, |
874 | QStringLiteral("location = %1) out " )); |
875 | } |
876 | |
877 | return shaderCode.toUtf8(); |
878 | } |
879 | |
880 | QByteArray QShaderProgramPrivate::resolveAutoBindingIndices(const QByteArray &content) |
881 | { |
882 | int currentBinding = 2; // Qt3D default uniforms are 0 and 1 |
883 | int currentInputLocation = 0; |
884 | int currentOutputLocation = 0; |
885 | |
886 | return QShaderProgramPrivate::resolveAutoBindingIndices(content, |
887 | currentBinding, |
888 | currentInputLocation, |
889 | currentOutputLocation); |
890 | } |
891 | |
892 | QByteArray QShaderProgramPrivate::deincludify(const QByteArray &contents, const QString &filePath) |
893 | { |
894 | QByteArrayList lines = contents.split(sep: '\n'); |
895 | const QByteArray includeDirective = QByteArrayLiteral("#pragma include" ); |
896 | for (int i = 0; i < lines.size(); ++i) { |
897 | const auto line = lines[i].simplified(); |
898 | if (line.startsWith(bv: includeDirective)) { |
899 | const QString includePartialPath = QString::fromUtf8(ba: line.mid(index: includeDirective.size() + 1)); |
900 | |
901 | QString includePath = QFileInfo(includePartialPath).isAbsolute() ? includePartialPath |
902 | : QFileInfo(filePath).absolutePath() + QLatin1Char('/') + includePartialPath; |
903 | if (qEnvironmentVariableIsSet(varName: "QT3D_GLSL100_WORKAROUND" )) { |
904 | QString candidate = includePath + QLatin1String("100" ); |
905 | if (QFile::exists(fileName: candidate)) |
906 | includePath = candidate; |
907 | } |
908 | lines.removeAt(i); |
909 | QByteArray includedContents = deincludify(filePath: includePath); |
910 | lines.insert(i, t: includedContents); |
911 | QString lineDirective = QString(QStringLiteral("#line %1" )).arg(a: i + 2); |
912 | lines.insert(i: i + 1, t: lineDirective.toUtf8()); |
913 | } |
914 | } |
915 | |
916 | return lines.join(sep: '\n'); |
917 | } |
918 | |
919 | /*! |
920 | \qmlmethod string ShaderProgram::loadSource(url sourceUrl) |
921 | |
922 | Returns the shader code loaded from \a sourceUrl. |
923 | */ |
924 | /*! |
925 | Returns the shader code loaded from \a sourceUrl. |
926 | */ |
927 | QByteArray QShaderProgram::loadSource(const QUrl &sourceUrl) |
928 | { |
929 | // TO DO: Handle remote path |
930 | const QByteArray deincluded = QShaderProgramPrivate::deincludify(filePath: Qt3DCore::QUrlHelper::urlToLocalFileOrQrc(url: sourceUrl)); |
931 | return QShaderProgramPrivate::resolveAutoBindingIndices(content: deincluded); |
932 | } |
933 | |
934 | } // of namespace Qt3DRender |
935 | |
936 | QT_END_NAMESPACE |
937 | |
938 | #include "moc_qshaderprogram.cpp" |
939 | |