1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick 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 "qsgmaterial.h"
41#include "qsgrenderer_p.h"
42#include "qsgmaterialshader_p.h"
43#if QT_CONFIG(opengl)
44# include <private/qsgshadersourcebuilder_p.h>
45# include <private/qsgdefaultcontext_p.h>
46# include <private/qsgdefaultrendercontext_p.h>
47# include <QtGui/QOpenGLFunctions>
48# include <QtGui/QOpenGLContext>
49#endif
50
51QT_BEGIN_NAMESPACE
52
53#if QT_CONFIG(opengl)
54const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
55{
56 const QStringList files = m_sourceFiles[type];
57 QSGShaderSourceBuilder builder;
58 for (const QString &file : files)
59 builder.appendSourceFile(fileName: file);
60 m_sources[type] = builder.source();
61 return m_sources[type].constData();
62}
63#endif
64
65/*!
66 \class QSGMaterialShader
67 \brief The QSGMaterialShader class represents an OpenGL shader program
68 in the renderer.
69 \inmodule QtQuick
70 \ingroup qtquick-scenegraph-materials
71
72 The QSGMaterialShader API is relatively low-level. A more convenient API,
73 which provides almost all the same features, is available through
74 QSGSimpleMaterialShader.
75
76 \warning This class is only functional when running with the legacy OpenGL
77 renderer of the Qt Quick scenegraph.
78
79 The QSGMaterial and QSGMaterialShader form a tight relationship. For one
80 scene graph (including nested graphs), there is one unique QSGMaterialShader
81 instance which encapsulates the QOpenGLShaderProgram the scene graph uses
82 to render that material, such as a shader to flat coloring of geometry.
83 Each QSGGeometryNode can have a unique QSGMaterial containing the
84 how the shader should be configured when drawing that node, such as
85 the actual color used to render the geometry.
86
87 An instance of QSGMaterialShader is never created explicitly by the user,
88 it will be created on demand by the scene graph through
89 QSGMaterial::createShader(). The scene graph will make sure that there
90 is only one instance of each shader implementation through a scene graph.
91
92 The source code returned from vertexShader() is used to control what the
93 material does with the vertiex data that comes in from the geometry.
94 The source code returned from the fragmentShader() is used to control
95 what how the material should fill each individual pixel in the geometry.
96 The vertex and fragment source code is queried once during initialization,
97 changing what is returned from these functions later will not have
98 any effect.
99
100 The activate() function is called by the scene graph when a shader is
101 is starting to be used. The deactivate function is called by the scene
102 graph when the shader is no longer going to be used. While active,
103 the scene graph may make one or more calls to updateState() which
104 will update the state of the shader for each individual geometry to
105 render.
106
107 The attributeNames() returns the name of the attributes used in the
108 vertexShader(). These are used in the default implementation of
109 activate() and deactivate() to decide whice vertex registers are enabled.
110
111 The initialize() function is called during program creation to allow
112 subclasses to prepare for use, such as resolve uniform names in the
113 vertexShader() and fragmentShader().
114
115 A minimal example:
116 \code
117 class Shader : public QSGMaterialShader
118 {
119 public:
120 const char *vertexShader() const {
121 return
122 "attribute highp vec4 vertex; \n"
123 "uniform highp mat4 matrix; \n"
124 "void main() { \n"
125 " gl_Position = matrix * vertex; \n"
126 "}";
127 }
128
129 const char *fragmentShader() const {
130 return
131 "uniform lowp float opacity; \n"
132 "void main() { \n"
133 " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
134 "}";
135 }
136
137 char const *const *attributeNames() const
138 {
139 static char const *const names[] = { "vertex", 0 };
140 return names;
141 }
142
143 void initialize()
144 {
145 QSGMaterialShader::initialize();
146 m_id_matrix = program()->uniformLocation("matrix");
147 m_id_opacity = program()->uniformLocation("opacity");
148 }
149
150 void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
151 {
152 Q_ASSERT(program()->isLinked());
153 if (state.isMatrixDirty())
154 program()->setUniformValue(m_id_matrix, state.combinedMatrix());
155 if (state.isOpacityDirty())
156 program()->setUniformValue(m_id_opacity, state.opacity());
157 }
158
159 private:
160 int m_id_matrix;
161 int m_id_opacity;
162 };
163 \endcode
164
165 \note All classes with QSG prefix should be used solely on the scene graph's
166 rendering thread. See \l {Scene Graph and Rendering} for more information.
167
168 */
169
170
171
172/*!
173 Creates a new QSGMaterialShader.
174 */
175QSGMaterialShader::QSGMaterialShader()
176 : d_ptr(new QSGMaterialShaderPrivate)
177{
178}
179
180/*!
181 \internal
182 */
183QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
184 : d_ptr(&dd)
185{
186}
187
188/*!
189 \internal
190 */
191QSGMaterialShader::~QSGMaterialShader()
192{
193}
194
195/*!
196 \fn char const *const *QSGMaterialShader::attributeNames() const
197
198 Returns a zero-terminated array describing the names of the
199 attributes used in the vertex shader.
200
201 This function is called when the shader is compiled to specify
202 which attributes exist. The order of the attribute names
203 defines the attribute register position in the vertex shader.
204 */
205
206#if QT_CONFIG(opengl)
207/*!
208 \fn const char *QSGMaterialShader::vertexShader() const
209
210 Called when the shader is being initialized to get the vertex
211 shader source code.
212
213 The contents returned from this function should never change.
214*/
215const char *QSGMaterialShader::vertexShader() const
216{
217 Q_D(const QSGMaterialShader);
218 return d->loadShaderSource(type: QOpenGLShader::Vertex);
219}
220
221
222/*!
223 \fn const char *QSGMaterialShader::fragmentShader() const
224
225 Called when the shader is being initialized to get the fragment
226 shader source code.
227
228 The contents returned from this function should never change.
229*/
230const char *QSGMaterialShader::fragmentShader() const
231{
232 Q_D(const QSGMaterialShader);
233 return d->loadShaderSource(type: QOpenGLShader::Fragment);
234}
235
236
237/*!
238 \fn QOpenGLShaderProgram *QSGMaterialShader::program()
239
240 Returns the shader program used by this QSGMaterialShader.
241 */
242#endif
243
244/*!
245 \fn void QSGMaterialShader::initialize()
246
247 Reimplement this function to do one-time initialization when the
248 shader program is compiled. The OpenGL shader program is compiled
249 and linked, but not bound, when this function is called.
250 */
251
252
253/*!
254 This function is called by the scene graph to indicate that geometry is
255 about to be rendered using this shader.
256
257 State that is global for all uses of the shader, independent of the geometry
258 that is being drawn, can be setup in this function.
259 */
260
261void QSGMaterialShader::activate()
262{
263}
264
265
266
267/*!
268 This function is called by the scene graph to indicate that geometry will
269 no longer to be rendered using this shader.
270 */
271
272void QSGMaterialShader::deactivate()
273{
274}
275
276
277
278/*!
279 This function is called by the scene graph before geometry is rendered
280 to make sure the shader is in the right state.
281
282 The current rendering \a state is passed from the scene graph. If the state
283 indicates that any state is dirty, the updateState implementation must
284 update accordingly for the geometry to render correctly.
285
286 The subclass specific state, such as the color of a flat color material, should
287 be extracted from \a newMaterial to update the color uniforms accordingly.
288
289 The \a oldMaterial can be used to minimze state changes when updating
290 material states. The \a oldMaterial is 0 if this shader was just activated.
291
292 \sa activate(), deactivate()
293 */
294
295void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
296{
297}
298
299#if QT_CONFIG(opengl)
300/*!
301 Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
302 default implementation of the vertexShader() and fragmentShader() functions
303 will load the source files set by this function.
304
305 This function is useful when you have a single source file for a given shader
306 stage. If your shader consists of multiple source files then use
307 setShaderSourceFiles()
308
309 \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
310 */
311void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
312{
313 Q_D(QSGMaterialShader);
314 d->m_sourceFiles[type] = (QStringList() << sourceFile);
315}
316
317/*!
318 Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
319 default implementation of the vertexShader() and fragmentShader() functions
320 will load the source files set by this function in the order given.
321
322 \sa setShaderSourceFile(), vertexShader(), fragmentShader()
323 */
324void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
325{
326 Q_D(QSGMaterialShader);
327 d->m_sourceFiles[type] = sourceFiles;
328}
329
330/*!
331 This function is called when the shader is initialized to compile the
332 actual QOpenGLShaderProgram. Do not call it explicitly.
333
334 The default implementation will extract the vertexShader() and
335 fragmentShader() and bind the names returned from attributeNames()
336 to consecutive vertex attribute registers starting at 0.
337 */
338
339void QSGMaterialShader::compile()
340{
341 Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
342
343 program()->addCacheableShaderFromSourceCode(type: QOpenGLShader::Vertex, source: vertexShader());
344 program()->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragmentShader());
345
346 char const *const *attr = attributeNames();
347#ifndef QT_NO_DEBUG
348 int maxVertexAttribs = 0;
349 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
350 funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, params: &maxVertexAttribs);
351 for (int i = 0; attr[i]; ++i) {
352 if (i >= maxVertexAttribs) {
353 qFatal(msg: "List of attribute names is either too long or not null-terminated.\n"
354 "Maximum number of attributes on this hardware is %i.\n"
355 "Vertex shader:\n%s\n"
356 "Fragment shader:\n%s\n",
357 maxVertexAttribs, vertexShader(), fragmentShader());
358 }
359 if (*attr[i])
360 program()->bindAttributeLocation(name: attr[i], location: i);
361 }
362#else
363 for (int i = 0; attr[i]; ++i) {
364 if (*attr[i])
365 program()->bindAttributeLocation(attr[i], i);
366 }
367#endif
368
369 if (!program()->link()) {
370 qWarning(msg: "QSGMaterialShader: Shader compilation failed:");
371 qWarning() << program()->log();
372 }
373}
374
375#endif
376
377/*!
378 \class QSGMaterialShader::RenderState
379 \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
380 during a call to QSGMaterialShader::updateState().
381 \inmodule QtQuick
382
383 The render state contains a number of accessors that the shader needs to respect
384 in order to conform to the current state of the scene graph.
385
386 The instance is only valid inside a call to QSGMaterialShader::updateState() and
387 should not be used outisde this function.
388 */
389
390
391
392/*!
393 \enum QSGMaterialShader::RenderState::DirtyState
394
395 \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
396
397 \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
398
399 \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
400
401 \value DirtyAll Used to indicate that everything needs to be updated.
402 */
403
404
405
406/*!
407 \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
408
409 Returns \c true if the dirtyStates() contain the dirty matrix state,
410 otherwise returns \c false.
411 */
412
413
414
415/*!
416 \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
417
418 Returns \c true if the dirtyStates() contains the dirty opacity state,
419 otherwise returns \c false.
420 */
421
422/*!
423 \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
424
425 Returns \c true if the dirtyStates() contains the dirty cached material state,
426 otherwise returns \c false.
427 */
428
429/*!
430 \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
431
432 Returns which rendering states that have changed and needs to be updated
433 for geometry rendered with this material to conform to the current
434 rendering state.
435 */
436
437
438
439/*!
440 Returns the accumulated opacity to be used for rendering.
441 */
442
443float QSGMaterialShader::RenderState::opacity() const
444{
445 Q_ASSERT(m_data);
446 return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
447}
448
449/*!
450 Returns the modelview determinant to be used for rendering.
451 */
452
453float QSGMaterialShader::RenderState::determinant() const
454{
455 Q_ASSERT(m_data);
456 return static_cast<const QSGRenderer *>(m_data)->determinant();
457}
458
459/*!
460 Returns the matrix combined of modelview matrix and project matrix.
461 */
462
463QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
464{
465 Q_ASSERT(m_data);
466 return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
467}
468/*!
469 Returns the ratio between physical pixels and device-independent pixels
470 to be used for rendering.
471*/
472float QSGMaterialShader::RenderState::devicePixelRatio() const
473{
474 Q_ASSERT(m_data);
475 return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
476}
477
478
479
480/*!
481 Returns the model view matrix.
482
483 If the material has the RequiresFullMatrix flag
484 set, this is guaranteed to be the complete transform
485 matrix calculated from the scenegraph.
486
487 However, if this flag is not set, the renderer may
488 choose to alter this matrix. For example, it may
489 pre-transform vertices on the CPU and set this matrix
490 to identity.
491
492 In a situation such as the above, it is still possible
493 to retrieve the actual matrix determinant by setting
494 the RequiresDeterminant flag in the material and
495 calling the determinant() accessor.
496 */
497
498QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
499{
500 Q_ASSERT(m_data);
501 return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
502}
503
504/*!
505 Returns the projection matrix.
506 */
507
508QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
509{
510 Q_ASSERT(m_data);
511 return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
512}
513
514
515
516/*!
517 Returns the viewport rect of the surface being rendered to.
518 */
519
520QRect QSGMaterialShader::RenderState::viewportRect() const
521{
522 Q_ASSERT(m_data);
523 return static_cast<const QSGRenderer *>(m_data)->viewportRect();
524}
525
526
527
528/*!
529 Returns the device rect of the surface being rendered to
530 */
531
532QRect QSGMaterialShader::RenderState::deviceRect() const
533{
534 Q_ASSERT(m_data);
535 return static_cast<const QSGRenderer *>(m_data)->deviceRect();
536}
537
538#if QT_CONFIG(opengl)
539
540/*!
541 Returns the QOpenGLContext that is being used for rendering
542 */
543
544QOpenGLContext *QSGMaterialShader::RenderState::context() const
545{
546 // Only the QSGDefaultRenderContext will have an OpenGL Context to query
547 auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
548 if (openGLRenderContext != nullptr)
549 return openGLRenderContext->openglContext();
550 else
551 return nullptr;
552}
553
554#endif
555
556QT_END_NAMESPACE
557

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp