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 | |
50 | inline static bool isPowerOfTwo(int x) |
51 | { |
52 | // Assumption: x >= 1 |
53 | return x == (x & -x); |
54 | } |
55 | |
56 | QSGOpaqueTextureMaterialShader::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 | |
64 | char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const |
65 | { |
66 | static char const *const attr[] = { "qt_VertexPosition" , "qt_VertexTexCoord" , nullptr }; |
67 | return attr; |
68 | } |
69 | |
70 | void QSGOpaqueTextureMaterialShader::initialize() |
71 | { |
72 | #if QT_CONFIG(opengl) |
73 | m_matrix_id = program()->uniformLocation(name: "qt_Matrix" ); |
74 | #endif |
75 | } |
76 | |
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(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 | |
122 | QSGOpaqueTextureMaterialRhiShader::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 | |
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(dest: buf->data(), src: m.constData(), n: 64); |
136 | changed = true; |
137 | } |
138 | |
139 | return changed; |
140 | } |
141 | |
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(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 | */ |
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(flags: SupportsRhiShader, on: true); |
230 | } |
231 | |
232 | |
233 | /*! |
234 | \internal |
235 | */ |
236 | QSGMaterialType *QSGOpaqueTextureMaterial::type() const |
237 | { |
238 | static QSGMaterialType type; |
239 | return &type; |
240 | } |
241 | |
242 | /*! |
243 | \internal |
244 | */ |
245 | QSGMaterialShader *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 | |
268 | void 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 | |
377 | 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 | |
425 | QSGMaterialType *QSGTextureMaterial::type() const |
426 | { |
427 | static QSGMaterialType type; |
428 | return &type; |
429 | } |
430 | |
431 | /*! |
432 | \internal |
433 | */ |
434 | |
435 | QSGMaterialShader *QSGTextureMaterial::createShader() const |
436 | { |
437 | if (flags().testFlag(flag: RhiShaderWanted)) |
438 | return new QSGTextureMaterialRhiShader; |
439 | else |
440 | return new QSGTextureMaterialShader; |
441 | } |
442 | |
443 | |
444 | QSGTextureMaterialShader::QSGTextureMaterialShader() |
445 | { |
446 | #if QT_CONFIG(opengl) |
447 | setShaderSourceFile(type: QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag" )); |
448 | #endif |
449 | } |
450 | |
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(location: m_opacity_id, value: state.opacity()); |
457 | #endif |
458 | QSGOpaqueTextureMaterialShader::updateState(state, newEffect, oldEffect); |
459 | } |
460 | |
461 | void QSGTextureMaterialShader::initialize() |
462 | { |
463 | QSGOpaqueTextureMaterialShader::initialize(); |
464 | #if QT_CONFIG(opengl) |
465 | m_opacity_id = program()->uniformLocation(name: "opacity" ); |
466 | #endif |
467 | } |
468 | |
469 | |
470 | QSGTextureMaterialRhiShader::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 | |
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(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 | |
492 | QT_END_NAMESPACE |
493 | |