1/****************************************************************************
2**
3** Copyright (C) 2016 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 "qsgtexturematerial_p.h"
41#include <private/qsgtexture_p.h>
42#if QT_CONFIG(opengl)
43# include <QtGui/qopenglshaderprogram.h>
44# include <QtGui/qopenglfunctions.h>
45#endif
46#include <QtGui/private/qrhi_p.h>
47
48QT_BEGIN_NAMESPACE
49
50inline static bool isPowerOfTwo(int x)
51{
52 // Assumption: x >= 1
53 return x == (x & -x);
54}
55
56QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader()
57{
58#if QT_CONFIG(opengl)
59 setShaderSourceFile(type: QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.vert"));
60 setShaderSourceFile(type: QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.frag"));
61#endif
62}
63
64char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const
65{
66 static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", nullptr };
67 return attr;
68}
69
70void QSGOpaqueTextureMaterialShader::initialize()
71{
72#if QT_CONFIG(opengl)
73 m_matrix_id = program()->uniformLocation(name: "qt_Matrix");
74#endif
75}
76
77void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
78{
79 Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
80 QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newEffect);
81 QSGOpaqueTextureMaterial *oldTx = static_cast<QSGOpaqueTextureMaterial *>(oldEffect);
82
83 QSGTexture *t = tx->texture();
84
85#ifndef QT_NO_DEBUG
86 if (!qsg_safeguard_texture(t))
87 return;
88#endif
89
90 t->setFiltering(tx->filtering());
91
92 t->setHorizontalWrapMode(tx->horizontalWrapMode());
93 t->setVerticalWrapMode(tx->verticalWrapMode());
94#if QT_CONFIG(opengl)
95 bool npotSupported = const_cast<QOpenGLContext *>(state.context())
96 ->functions()->hasOpenGLFeature(feature: QOpenGLFunctions::NPOTTextureRepeat);
97 if (!npotSupported) {
98 QSize size = t->textureSize();
99 const bool isNpot = !isPowerOfTwo(x: size.width()) || !isPowerOfTwo(x: size.height());
100 if (isNpot) {
101 t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
102 t->setVerticalWrapMode(QSGTexture::ClampToEdge);
103 }
104 }
105#else
106 Q_UNUSED(state)
107#endif
108 t->setMipmapFiltering(tx->mipmapFiltering());
109 t->setAnisotropyLevel(tx->anisotropyLevel());
110
111 if (oldTx == nullptr || oldTx->texture()->textureId() != t->textureId())
112 t->bind();
113 else
114 t->updateBindOptions();
115#if QT_CONFIG(opengl)
116 if (state.isMatrixDirty())
117 program()->setUniformValue(location: m_matrix_id, value: state.combinedMatrix());
118#endif
119}
120
121
122QSGOpaqueTextureMaterialRhiShader::QSGOpaqueTextureMaterialRhiShader()
123{
124 setShaderFileName(stage: VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.vert.qsb"));
125 setShaderFileName(stage: FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.frag.qsb"));
126}
127
128bool QSGOpaqueTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *)
129{
130 bool changed = false;
131 QByteArray *buf = state.uniformData();
132
133 if (state.isMatrixDirty()) {
134 const QMatrix4x4 m = state.combinedMatrix();
135 memcpy(dest: buf->data(), src: m.constData(), n: 64);
136 changed = true;
137 }
138
139 return changed;
140}
141
142void QSGOpaqueTextureMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
143 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
144{
145 if (binding != 1)
146 return;
147
148#ifdef QT_NO_DEBUG
149 Q_UNUSED(oldMaterial);
150#endif
151 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
152 QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newMaterial);
153 QSGTexture *t = tx->texture();
154
155 t->setFiltering(tx->filtering());
156 t->setMipmapFiltering(tx->mipmapFiltering());
157 t->setAnisotropyLevel(tx->anisotropyLevel());
158
159 t->setHorizontalWrapMode(tx->horizontalWrapMode());
160 t->setVerticalWrapMode(tx->verticalWrapMode());
161 if (!state.rhi()->isFeatureSupported(feature: QRhi::NPOTTextureRepeat)) {
162 QSize size = t->textureSize();
163 const bool isNpot = !isPowerOfTwo(x: size.width()) || !isPowerOfTwo(x: size.height());
164 if (isNpot) {
165 t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
166 t->setVerticalWrapMode(QSGTexture::ClampToEdge);
167 t->setMipmapFiltering(QSGTexture::None);
168 }
169 }
170
171 t->updateRhiTexture(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
172 *texture = t;
173}
174
175
176/*!
177 \class QSGOpaqueTextureMaterial
178 \brief The QSGOpaqueTextureMaterial class provides a convenient way of
179 rendering textured geometry in the scene graph.
180 \inmodule QtQuick
181 \ingroup qtquick-scenegraph-materials
182
183 \warning This utility class is only functional when running with the
184 default backend of the Qt Quick scenegraph.
185
186 The opaque textured material will fill every pixel in a geometry with
187 the supplied texture. The material does not respect the opacity of the
188 QSGMaterialShader::RenderState, so opacity nodes in the parent chain
189 of nodes using this material, have no effect.
190
191 The geometry to be rendered with an opaque texture material requires
192 vertices in attribute location 0 and texture coordinates in attribute
193 location 1. The texture coordinate is a 2-dimensional floating-point
194 tuple. The QSGGeometry::defaultAttributes_TexturedPoint2D returns an
195 attribute set compatible with this material.
196
197 The texture to be rendered can be set using setTexture(). How the
198 texture should be rendered can be specified using setMipmapFiltering(),
199 setFiltering(), setHorizontalWrapMode() and setVerticalWrapMode().
200 The rendering state is set on the texture instance just before it
201 is bound.
202
203 The opaque textured material respects the current matrix and the alpha
204 channel of the texture. It will disregard the accumulated opacity in
205 the scenegraph.
206
207 A texture material must have a texture set before it is used as
208 a material in the scene graph.
209 */
210
211
212
213/*!
214 Creates a new QSGOpaqueTextureMaterial.
215
216 The default mipmap filtering and filtering mode is set to
217 QSGTexture::Nearest. The default wrap modes is set to
218 \c QSGTexture::ClampToEdge.
219
220 */
221QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial()
222 : m_texture(nullptr)
223 , m_filtering(QSGTexture::Nearest)
224 , m_mipmap_filtering(QSGTexture::None)
225 , m_horizontal_wrap(QSGTexture::ClampToEdge)
226 , m_vertical_wrap(QSGTexture::ClampToEdge)
227 , m_anisotropy_level(QSGTexture::AnisotropyNone)
228{
229 setFlag(flags: SupportsRhiShader, on: true);
230}
231
232
233/*!
234 \internal
235 */
236QSGMaterialType *QSGOpaqueTextureMaterial::type() const
237{
238 static QSGMaterialType type;
239 return &type;
240}
241
242/*!
243 \internal
244 */
245QSGMaterialShader *QSGOpaqueTextureMaterial::createShader() const
246{
247 if (flags().testFlag(flag: RhiShaderWanted))
248 return new QSGOpaqueTextureMaterialRhiShader;
249 else
250 return new QSGOpaqueTextureMaterialShader;
251}
252
253
254/*!
255 \fn QSGTexture *QSGOpaqueTextureMaterial::texture() const
256
257 Returns this texture material's texture.
258 */
259
260
261
262/*!
263 Sets the texture of this material to \a texture.
264
265 The material does not take ownership of the texture.
266 */
267
268void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture)
269{
270 m_texture = texture;
271 setFlag(flags: Blending, on: m_texture ? m_texture->hasAlphaChannel() : false);
272}
273
274
275
276/*!
277 \fn void QSGOpaqueTextureMaterial::setMipmapFiltering(QSGTexture::Filtering filtering)
278
279 Sets the mipmap mode to \a filtering.
280
281 The mipmap filtering mode is set on the texture instance just before the
282 texture is bound for rendering.
283
284 If the texture does not have mipmapping support, enabling mipmapping has no
285 effect.
286 */
287
288
289
290/*!
291 \fn QSGTexture::Filtering QSGOpaqueTextureMaterial::mipmapFiltering() const
292
293 Returns this material's mipmap filtering mode.
294
295 The default mipmap mode is \c QSGTexture::Nearest.
296 */
297
298
299
300/*!
301 \fn void QSGOpaqueTextureMaterial::setFiltering(QSGTexture::Filtering filtering)
302
303 Sets the filtering to \a filtering.
304
305 The filtering mode is set on the texture instance just before the texture
306 is bound for rendering.
307 */
308
309
310
311/*!
312 \fn QSGTexture::Filtering QSGOpaqueTextureMaterial::filtering() const
313
314 Returns this material's filtering mode.
315
316 The default filtering is \c QSGTexture::Nearest.
317 */
318
319
320
321/*!
322 \fn void QSGOpaqueTextureMaterial::setHorizontalWrapMode(QSGTexture::WrapMode mode)
323
324 Sets the horizontal wrap mode to \a mode.
325
326 The horizontal wrap mode is set on the texture instance just before the texture
327 is bound for rendering.
328 */
329
330
331
332 /*!
333 \fn QSGTexture::WrapMode QSGOpaqueTextureMaterial::horizontalWrapMode() const
334
335 Returns this material's horizontal wrap mode.
336
337 The default horizontal wrap mode is \c QSGTexture::ClampToEdge.
338 */
339
340
341
342/*!
343 \fn void QSGOpaqueTextureMaterial::setVerticalWrapMode(QSGTexture::WrapMode mode)
344
345 Sets the vertical wrap mode to \a mode.
346
347 The vertical wrap mode is set on the texture instance just before the texture
348 is bound for rendering.
349 */
350
351
352
353 /*!
354 \fn QSGTexture::WrapMode QSGOpaqueTextureMaterial::verticalWrapMode() const
355
356 Returns this material's vertical wrap mode.
357
358 The default vertical wrap mode is \c QSGTexture::ClampToEdge.
359 */
360
361/*!
362 \fn void QSGOpaqueTextureMaterial::setAnisotropyLevel(QSGTexture::AnisotropyLevel level)
363
364 Sets this material's anistropy level to \a level.
365*/
366
367/*!
368 \fn QSGTexture::AnisotropyLevel QSGOpaqueTextureMaterial::anisotropyLevel() const
369
370 Returns this material's anistropy level.
371*/
372
373/*!
374 \internal
375 */
376
377int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
378{
379 Q_ASSERT(o && type() == o->type());
380 const QSGOpaqueTextureMaterial *other = static_cast<const QSGOpaqueTextureMaterial *>(o);
381 if (int diff = m_texture->comparisonKey() - other->texture()->comparisonKey())
382 return diff;
383 return int(m_filtering) - int(other->m_filtering);
384}
385
386
387
388/*!
389 \class QSGTextureMaterial
390 \brief The QSGTextureMaterial class provides a convenient way of
391 rendering textured geometry in the scene graph.
392 \inmodule QtQuick
393 \ingroup qtquick-scenegraph-materials
394
395 \warning This utility class is only functional when running with the
396 default backend of the Qt Quick scenegraph.
397
398 The textured material will fill every pixel in a geometry with
399 the supplied texture.
400
401 The geometry to be rendered with a texture material requires
402 vertices in attribute location 0 and texture coordinates in attribute
403 location 1. The texture coordinate is a 2-dimensional floating-point
404 tuple. The QSGGeometry::defaultAttributes_TexturedPoint2D returns an
405 attribute set compatible with this material.
406
407 The texture to be rendered can be set using setTexture(). How the
408 texture should be rendered can be specified using setMipmapFiltering(),
409 setFiltering(), setHorizontalWrapMode() and setVerticalWrapMode().
410 The rendering state is set on the texture instance just before it
411 is bound.
412
413 The textured material respects the current matrix and the alpha
414 channel of the texture. It will also respect the accumulated opacity
415 in the scenegraph.
416
417 A texture material must have a texture set before it is used as
418 a material in the scene graph.
419 */
420
421/*!
422 \internal
423 */
424
425QSGMaterialType *QSGTextureMaterial::type() const
426{
427 static QSGMaterialType type;
428 return &type;
429}
430
431/*!
432 \internal
433 */
434
435QSGMaterialShader *QSGTextureMaterial::createShader() const
436{
437 if (flags().testFlag(flag: RhiShaderWanted))
438 return new QSGTextureMaterialRhiShader;
439 else
440 return new QSGTextureMaterialShader;
441}
442
443
444QSGTextureMaterialShader::QSGTextureMaterialShader()
445{
446#if QT_CONFIG(opengl)
447 setShaderSourceFile(type: QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag"));
448#endif
449}
450
451void QSGTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
452{
453 Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
454#if QT_CONFIG(opengl)
455 if (state.isOpacityDirty())
456 program()->setUniformValue(location: m_opacity_id, value: state.opacity());
457#endif
458 QSGOpaqueTextureMaterialShader::updateState(state, newEffect, oldEffect);
459}
460
461void QSGTextureMaterialShader::initialize()
462{
463 QSGOpaqueTextureMaterialShader::initialize();
464#if QT_CONFIG(opengl)
465 m_opacity_id = program()->uniformLocation(name: "opacity");
466#endif
467}
468
469
470QSGTextureMaterialRhiShader::QSGTextureMaterialRhiShader()
471{
472 setShaderFileName(stage: VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.vert.qsb"));
473 setShaderFileName(stage: FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.frag.qsb"));
474}
475
476bool QSGTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
477{
478 bool changed = false;
479 QByteArray *buf = state.uniformData();
480
481 if (state.isOpacityDirty()) {
482 const float opacity = state.opacity();
483 memcpy(dest: buf->data() + 64, src: &opacity, n: 4);
484 changed = true;
485 }
486
487 changed |= QSGOpaqueTextureMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
488
489 return changed;
490}
491
492QT_END_NAMESPACE
493

source code of qtdeclarative/src/quick/scenegraph/util/qsgtexturematerial.cpp