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 
48 QT_BEGIN_NAMESPACE
49 
isPowerOfTwo(int x)50 inline static bool isPowerOfTwo(int x)
51 {
52     // Assumption: x >= 1
53     return x == (x & -x);
54 }
55 
QSGOpaqueTextureMaterialShader()56 QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader()
57 {
58 #if QT_CONFIG(opengl)
59     setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.vert"));
60     setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.frag"));
61 #endif
62 }
63 
attributeNames() const64 char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const
65 {
66     static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", nullptr };
67     return attr;
68 }
69 
initialize()70 void QSGOpaqueTextureMaterialShader::initialize()
71 {
72 #if QT_CONFIG(opengl)
73     m_matrix_id = program()->uniformLocation("qt_Matrix");
74 #endif
75 }
76 
updateState(const RenderState & state,QSGMaterial * newEffect,QSGMaterial * oldEffect)77 void 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(QOpenGLFunctions::NPOTTextureRepeat);
97     if (!npotSupported) {
98         QSize size = t->textureSize();
99         const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(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(m_matrix_id, state.combinedMatrix());
118 #endif
119 }
120 
121 
QSGOpaqueTextureMaterialRhiShader()122 QSGOpaqueTextureMaterialRhiShader::QSGOpaqueTextureMaterialRhiShader()
123 {
124     setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.vert.qsb"));
125     setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.frag.qsb"));
126 }
127 
updateUniformData(RenderState & state,QSGMaterial *,QSGMaterial *)128 bool 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(buf->data(), m.constData(), 64);
136         changed = true;
137     }
138 
139     return changed;
140 }
141 
updateSampledImage(RenderState & state,int binding,QSGTexture ** texture,QSGMaterial * newMaterial,QSGMaterial * oldMaterial)142 void 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(QRhi::NPOTTextureRepeat)) {
162         QSize size = t->textureSize();
163         const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(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(state.rhi(), 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  */
QSGOpaqueTextureMaterial()221 QSGOpaqueTextureMaterial::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(SupportsRhiShader, true);
230 }
231 
232 
233 /*!
234     \internal
235  */
type() const236 QSGMaterialType *QSGOpaqueTextureMaterial::type() const
237 {
238     static QSGMaterialType type;
239     return &type;
240 }
241 
242 /*!
243     \internal
244  */
createShader() const245 QSGMaterialShader *QSGOpaqueTextureMaterial::createShader() const
246 {
247     if (flags().testFlag(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 
setTexture(QSGTexture * texture)268 void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture)
269 {
270     m_texture = texture;
271     setFlag(Blending, 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 
compare(const QSGMaterial * o) const377 int 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 
type() const425 QSGMaterialType *QSGTextureMaterial::type() const
426 {
427     static QSGMaterialType type;
428     return &type;
429 }
430 
431 /*!
432     \internal
433  */
434 
createShader() const435 QSGMaterialShader *QSGTextureMaterial::createShader() const
436 {
437     if (flags().testFlag(RhiShaderWanted))
438         return new QSGTextureMaterialRhiShader;
439     else
440         return new QSGTextureMaterialShader;
441 }
442 
443 
QSGTextureMaterialShader()444 QSGTextureMaterialShader::QSGTextureMaterialShader()
445 {
446 #if QT_CONFIG(opengl)
447     setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag"));
448 #endif
449 }
450 
updateState(const RenderState & state,QSGMaterial * newEffect,QSGMaterial * oldEffect)451 void 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(m_opacity_id, state.opacity());
457 #endif
458     QSGOpaqueTextureMaterialShader::updateState(state, newEffect, oldEffect);
459 }
460 
initialize()461 void QSGTextureMaterialShader::initialize()
462 {
463     QSGOpaqueTextureMaterialShader::initialize();
464 #if QT_CONFIG(opengl)
465     m_opacity_id = program()->uniformLocation("opacity");
466 #endif
467 }
468 
469 
QSGTextureMaterialRhiShader()470 QSGTextureMaterialRhiShader::QSGTextureMaterialRhiShader()
471 {
472     setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.vert.qsb"));
473     setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.frag.qsb"));
474 }
475 
updateUniformData(RenderState & state,QSGMaterial * newMaterial,QSGMaterial * oldMaterial)476 bool 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(buf->data() + 64, &opacity, 4);
484         changed = true;
485     }
486 
487     changed |= QSGOpaqueTextureMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
488 
489     return changed;
490 }
491 
492 QT_END_NAMESPACE
493