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 "qsgtexture_p.h"
41#if QT_CONFIG(opengl)
42# include <QtGui/qopenglcontext.h>
43# include <QtGui/qopenglfunctions.h>
44#endif
45#include <private/qqmlglobal_p.h>
46#include <private/qsgmaterialshader_p.h>
47#include <QtGui/private/qrhi_p.h>
48
49#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__)
50#define CAN_BACKTRACE_EXECINFO
51#endif
52
53#if defined(Q_OS_MAC)
54#define CAN_BACKTRACE_EXECINFO
55#endif
56
57#if defined(QT_NO_DEBUG)
58#undef CAN_BACKTRACE_EXECINFO
59#endif
60
61#if defined(CAN_BACKTRACE_EXECINFO)
62#include <execinfo.h>
63#include <QHash>
64#endif
65
66#ifndef QT_NO_DEBUG
67static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty(varName: "QML_LEAK_CHECK");
68#endif
69
70QT_BEGIN_NAMESPACE
71
72bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
73{
74 return a.filtering == b.filtering
75 && a.mipmapFiltering == b.mipmapFiltering
76 && a.horizontalWrap == b.horizontalWrap
77 && a.verticalWrap == b.verticalWrap
78 && a.anisotropylevel == b.anisotropylevel;
79}
80
81bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
82{
83 return !(a == b);
84}
85
86uint qHash(const QSGSamplerDescription &s, uint seed) Q_DECL_NOTHROW
87{
88 const int f = s.filtering;
89 const int m = s.mipmapFiltering;
90 const int w = s.horizontalWrap;
91 const int a = s.anisotropylevel;
92 return (((f & 7) << 24) | ((m & 7) << 16) | ((w & 7) << 8) | (a & 7)) ^ seed;
93}
94
95QSGSamplerDescription QSGSamplerDescription::fromTexture(QSGTexture *t)
96{
97 QSGSamplerDescription s;
98 s.filtering = t->filtering();
99 s.mipmapFiltering = t->mipmapFiltering();
100 s.horizontalWrap = t->horizontalWrapMode();
101 s.verticalWrap = t->verticalWrapMode();
102 s.anisotropylevel = t->anisotropyLevel();
103 return s;
104}
105
106#if QT_CONFIG(opengl)
107#ifndef QT_NO_DEBUG
108inline static bool isPowerOfTwo(int x)
109{
110 // Assumption: x >= 1
111 return x == (x & -x);
112}
113#endif
114#endif
115
116QSGTexturePrivate::QSGTexturePrivate()
117 : wrapChanged(false)
118 , filteringChanged(false)
119 , anisotropyChanged(false)
120 , horizontalWrap(QSGTexture::ClampToEdge)
121 , verticalWrap(QSGTexture::ClampToEdge)
122 , mipmapMode(QSGTexture::None)
123 , filterMode(QSGTexture::Nearest)
124 , anisotropyLevel(QSGTexture::AnisotropyNone)
125{
126}
127
128#ifndef QT_NO_DEBUG
129
130static int qt_debug_texture_count = 0;
131
132#if (defined(Q_OS_LINUX) || defined (Q_OS_MAC)) && !defined(Q_OS_ANDROID)
133DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE)
134
135#define BACKTRACE_SIZE 20
136class SGTextureTraceItem
137{
138public:
139 void *backTrace[BACKTRACE_SIZE];
140 size_t backTraceSize;
141};
142
143static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures;
144#endif
145
146inline static void qt_debug_print_texture_count()
147{
148 qDebug(msg: "Number of leaked textures: %i", qt_debug_texture_count);
149 qt_debug_texture_count = -1;
150
151#if defined(CAN_BACKTRACE_EXECINFO)
152 if (qmlDebugLeakBacktrace()) {
153 while (!qt_debug_allocated_textures.isEmpty()) {
154 QHash<QSGTexture*, SGTextureTraceItem*>::Iterator it = qt_debug_allocated_textures.begin();
155 QSGTexture* texture = it.key();
156 SGTextureTraceItem* item = it.value();
157
158 qt_debug_allocated_textures.erase(it);
159
160 qDebug() << "------";
161 qDebug() << "Leaked" << texture << "backtrace:";
162
163 char** symbols = backtrace_symbols(array: item->backTrace, size: item->backTraceSize);
164
165 if (symbols) {
166 for (int i=0; i<(int) item->backTraceSize; i++)
167 qDebug(msg: "Backtrace <%02d>: %s", i, symbols[i]);
168 free(ptr: symbols);
169 }
170
171 qDebug() << "------";
172
173 delete item;
174 }
175 }
176#endif
177}
178
179inline static void qt_debug_add_texture(QSGTexture* texture)
180{
181#if defined(CAN_BACKTRACE_EXECINFO)
182 if (qmlDebugLeakBacktrace()) {
183 SGTextureTraceItem* item = new SGTextureTraceItem;
184 item->backTraceSize = backtrace(array: item->backTrace, BACKTRACE_SIZE);
185 qt_debug_allocated_textures.insert(key: texture, value: item);
186 }
187#else
188 Q_UNUSED(texture);
189#endif // Q_OS_LINUX
190
191 ++qt_debug_texture_count;
192
193 static bool atexit_registered = false;
194 if (!atexit_registered) {
195 atexit(func: qt_debug_print_texture_count);
196 atexit_registered = true;
197 }
198}
199
200static void qt_debug_remove_texture(QSGTexture* texture)
201{
202#if defined(CAN_BACKTRACE_EXECINFO)
203 if (qmlDebugLeakBacktrace()) {
204 SGTextureTraceItem* item = qt_debug_allocated_textures.value(key: texture, defaultValue: 0);
205 if (item) {
206 qt_debug_allocated_textures.remove(key: texture);
207 delete item;
208 }
209 }
210#else
211 Q_UNUSED(texture)
212#endif
213
214 --qt_debug_texture_count;
215
216 if (qt_debug_texture_count < 0)
217 qDebug(msg: "Texture destroyed after qt_debug_print_texture_count() was called.");
218}
219
220#endif // QT_NO_DEBUG
221
222/*!
223 \class QSGTexture
224
225 \inmodule QtQuick
226
227 \brief The QSGTexture class is a baseclass for textures used in
228 the scene graph.
229
230
231 Users can freely implement their own texture classes to support
232 arbitrary input textures, such as YUV video frames or 8 bit alpha
233 masks. The scene graph backend provides a default implementation
234 of normal color textures. As the implementation of these may be
235 hardware specific, they are constructed via the factory
236 function QQuickWindow::createTextureFromImage().
237
238 The texture is a wrapper around an OpenGL texture, which texture
239 id is given by textureId() and which size in pixels is given by
240 textureSize(). hasAlphaChannel() reports if the texture contains
241 opacity values and hasMipmaps() reports if the texture contains
242 mipmap levels.
243
244 To use a texture, call the bind() function. The texture parameters
245 specifying how the texture is bound, can be specified with
246 setMipmapFiltering(), setFiltering(), setHorizontalWrapMode() and
247 setVerticalWrapMode(). The texture will internally try to store
248 these values to minimize the OpenGL state changes when the texture
249 is bound.
250
251 \section1 Texture Atlasses
252
253 Some scene graph backends use texture atlasses, grouping multiple
254 small textures into one large texture. If this is the case, the
255 function isAtlasTexture() will return true. Atlasses are used to
256 aid the rendering algorithm to do better sorting which increases
257 performance. The location of the texture inside the atlas is
258 given with the normalizedTextureSubRect() function.
259
260 If the texture is used in such a way that atlas is not preferable,
261 the function removedFromAtlas() can be used to extract a
262 non-atlassed copy.
263
264 \note All classes with QSG prefix should be used solely on the scene graph's
265 rendering thread. See \l {Scene Graph and Rendering} for more information.
266
267 \sa {Scene Graph - Rendering FBOs}, {Scene Graph - Rendering FBOs in a thread}
268 */
269
270/*!
271 \enum QSGTexture::WrapMode
272
273 Specifies how the texture should treat texture coordinates.
274
275 \value Repeat Only the fractional part of the texture coordinate is
276 used, causing values above 1 and below 0 to repeat.
277
278 \value ClampToEdge Values above 1 are clamped to 1 and values
279 below 0 are clamped to 0.
280
281 \value MirroredRepeat When the texture coordinate is even, only the
282 fractional part is used. When odd, the texture coordinate is set to
283 \c{1 - fractional part}. This value has been introduced in Qt 5.10.
284 */
285
286/*!
287 \enum QSGTexture::Filtering
288
289 Specifies how sampling of texels should filter when texture
290 coordinates are not pixel aligned.
291
292 \value None No filtering should occur. This value is only used
293 together with setMipmapFiltering().
294
295 \value Nearest Sampling returns the nearest texel.
296
297 \value Linear Sampling returns a linear interpolation of the
298 neighboring texels.
299*/
300
301/*!
302 \enum QSGTexture::AnisotropyLevel
303
304 Specifies the anisotropic filtering level to be used when
305 the texture is not screen aligned.
306
307 \value AnisotropyNone No anisotropic filtering.
308
309 \value Anisotropy2x 2x anisotropic filtering.
310
311 \value Anisotropy4x 4x anisotropic filtering.
312
313 \value Anisotropy8x 8x anisotropic filtering.
314
315 \value Anisotropy16x 16x anisotropic filtering.
316
317 \since 5.9
318*/
319
320/*!
321 \class QSGTexture::NativeTexture
322 \brief Contains information about the underlying native resources of a texture.
323 \since 5.15
324 */
325
326/*!
327 \variable QSGTexture::NativeTexture::object
328 \brief a pointer to the native object handle.
329
330 With OpenGL, the native handle is a GLuint value, so \c object is then a
331 pointer to a GLuint. With Vulkan, the native handle is a VkImage, so \c
332 object is a pointer to a VkImage. With Direct3D 11 and Metal \c
333 object is a pointer to a ID3D11Texture2D or MTLTexture pointer, respectively.
334
335 \note Pay attention to the fact that \a object is always a pointer
336 to the native texture handle type, even if the native type itself is a
337 pointer.
338 */
339
340/*!
341 \variable QSGTexture::NativeTexture::layout
342 \brief Specifies the current image layout for APIs like Vulkan.
343
344 For Vulkan, \c layout contains a \c VkImageLayout value.
345 */
346
347
348#ifndef QT_NO_DEBUG
349Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
350#endif
351
352#ifndef QT_NO_DEBUG
353Q_GLOBAL_STATIC(QSet<QSGTexture *>, qsg_valid_texture_set)
354Q_GLOBAL_STATIC(QMutex, qsg_valid_texture_mutex)
355
356bool qsg_safeguard_texture(QSGTexture *texture)
357{
358#if QT_CONFIG(opengl)
359 QMutexLocker locker(qsg_valid_texture_mutex());
360 if (!qsg_valid_texture_set()->contains(value: texture)) {
361 qWarning() << "Invalid texture accessed:" << (void *) texture;
362 qsg_set_material_failure();
363 QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, texture: 0);
364 return false;
365 }
366#else
367 Q_UNUSED(texture)
368#endif
369 return true;
370}
371#endif
372
373/*!
374 Constructs the QSGTexture base class.
375 */
376QSGTexture::QSGTexture()
377 : QObject(*(new QSGTexturePrivate))
378{
379#ifndef QT_NO_DEBUG
380 if (qsg_leak_check)
381 qt_debug_add_texture(texture: this);
382
383 QMutexLocker locker(qsg_valid_texture_mutex());
384 qsg_valid_texture_set()->insert(value: this);
385#endif
386}
387
388/*!
389 \internal
390 */
391QSGTexture::QSGTexture(QSGTexturePrivate &dd)
392 : QObject(dd)
393{
394#ifndef QT_NO_DEBUG
395 if (qsg_leak_check)
396 qt_debug_add_texture(texture: this);
397
398 QMutexLocker locker(qsg_valid_texture_mutex());
399 qsg_valid_texture_set()->insert(value: this);
400#endif
401}
402
403/*!
404 Destroys the QSGTexture.
405 */
406QSGTexture::~QSGTexture()
407{
408#ifndef QT_NO_DEBUG
409 if (qsg_leak_check)
410 qt_debug_remove_texture(texture: this);
411
412 QMutexLocker locker(qsg_valid_texture_mutex());
413 qsg_valid_texture_set()->remove(value: this);
414#endif
415}
416
417/*!
418 \fn void QSGTexture::bind()
419
420 Call this function to bind this texture to the current texture
421 target.
422
423 Binding a texture may also include uploading the texture data from
424 a previously set QImage.
425
426 \warning This function should only be called when running with the
427 direct OpenGL rendering path.
428
429 \warning This function can only be called from the rendering thread.
430 */
431
432/*!
433 \fn QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const
434
435 Returns \a rect converted to normalized coordinates.
436
437 \sa normalizedTextureSubRect()
438 */
439
440/*!
441 This function returns a copy of the current texture which is removed
442 from its atlas.
443
444 The current texture remains unchanged, so texture coordinates do not
445 need to be updated.
446
447 Removing a texture from an atlas is primarily useful when passing
448 it to a shader that operates on the texture coordinates 0-1 instead
449 of the texture subrect inside the atlas.
450
451 If the texture is not part of a texture atlas, this function returns 0.
452
453 Implementations of this function are recommended to return the same instance
454 for multiple calls to limit memory usage.
455
456 \warning This function can only be called from the rendering thread.
457 */
458
459QSGTexture *QSGTexture::removedFromAtlas() const
460{
461 Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture");
462 return nullptr;
463}
464
465/*!
466 Returns weither this texture is part of an atlas or not.
467
468 The default implementation returns false.
469 */
470bool QSGTexture::isAtlasTexture() const
471{
472 return false;
473}
474
475/*!
476 \fn int QSGTexture::textureId() const
477
478 Returns the OpenGL texture id for this texture.
479
480 The default value is 0, indicating that it is an invalid texture id.
481
482 The function should at all times return the correct texture id.
483
484 \warning This function can only be called from the rendering thread.
485 */
486
487/*!
488 Returns a key suitable for comparing textures. Typically used in
489 QSGMaterial::compare() implementations.
490
491 Just comparing QSGTexture pointers is not always sufficient because two
492 QSGTexture instances that refer to the same native texture object
493 underneath should also be considered equal. Hence this function.
494
495 \note Unlike textureId(), implementations of this function are not expected
496 to and should not create any graphics resources (so texture objects) in
497 case there is none yet.
498
499 A QSGTexture that does not have a native texture object underneath is
500 typically not equal to any other QSGTexture. There are exceptions to this,
501 in particular when atlasing is used (where multiple textures share the same
502 atlas texture under the hood), that is then up to the subclass
503 implementations to deal with as appropriate.
504
505 \warning This function can only be called from the rendering thread.
506
507 \since 5.14
508 */
509int QSGTexture::comparisonKey() const
510{
511 Q_D(const QSGTexture);
512 return d->comparisonKey();
513}
514
515/*!
516 \fn QSize QSGTexture::textureSize() const
517
518 Returns the size of the texture.
519 */
520
521/*!
522 Returns the rectangle inside textureSize() that this texture
523 represents in normalized coordinates.
524
525 The default implementation returns a rect at position (0, 0) with
526 width and height of 1.
527 */
528QRectF QSGTexture::normalizedTextureSubRect() const
529{
530 return QRectF(0, 0, 1, 1);
531}
532
533/*!
534 \fn bool QSGTexture::hasAlphaChannel() const
535
536 Returns true if the texture data contains an alpha channel.
537 */
538
539/*!
540 \fn bool QSGTexture::hasMipmaps() const
541
542 Returns true if the texture data contains mipmap levels.
543 */
544
545
546/*!
547 Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
548
549 Setting the mipmap filtering has no effect it the texture does not have mipmaps.
550
551 \sa hasMipmaps()
552 */
553void QSGTexture::setMipmapFiltering(Filtering filter)
554{
555 Q_D(QSGTexture);
556 if (d->mipmapMode != (uint) filter) {
557 d->mipmapMode = filter;
558 d->filteringChanged = true;
559 }
560}
561
562/*!
563 Returns whether mipmapping should be used when sampling from this texture.
564 */
565QSGTexture::Filtering QSGTexture::mipmapFiltering() const
566{
567 return (QSGTexture::Filtering) d_func()->mipmapMode;
568}
569
570
571/*!
572 Sets the sampling mode to be used for the upcoming bind() call to \a filter.
573 */
574void QSGTexture::setFiltering(QSGTexture::Filtering filter)
575{
576 Q_D(QSGTexture);
577 if (d->filterMode != (uint) filter) {
578 d->filterMode = filter;
579 d->filteringChanged = true;
580 }
581}
582
583/*!
584 Returns the sampling mode to be used for this texture.
585 */
586QSGTexture::Filtering QSGTexture::filtering() const
587{
588 return (QSGTexture::Filtering) d_func()->filterMode;
589}
590
591/*!
592 Sets the level of anisotropic filtering to be used for the upcoming bind() call to \a level.
593 The default value is QSGTexture::AnisotropyNone, which means no anisotropic filtering is enabled.
594
595 \since 5.9
596 */
597void QSGTexture::setAnisotropyLevel(AnisotropyLevel level)
598{
599 Q_D(QSGTexture);
600 if (d->anisotropyLevel != (uint) level) {
601 d->anisotropyLevel = level;
602 d->anisotropyChanged = true;
603 }
604}
605
606/*!
607 Returns the anisotropy level in use for filtering this texture.
608
609 \since 5.9
610 */
611QSGTexture::AnisotropyLevel QSGTexture::anisotropyLevel() const
612{
613 return (QSGTexture::AnisotropyLevel) d_func()->anisotropyLevel;
614}
615
616
617
618/*!
619 Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
620 */
621
622void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
623{
624 Q_D(QSGTexture);
625 if ((uint) hwrap != d->horizontalWrap) {
626 d->horizontalWrap = hwrap;
627 d->wrapChanged = true;
628 }
629}
630
631/*!
632 Returns the horizontal wrap mode to be used for this texture.
633 */
634QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
635{
636 return (QSGTexture::WrapMode) d_func()->horizontalWrap;
637}
638
639
640
641/*!
642 Sets the vertical wrap mode to be used for the upcoming bind() call to \a vwrap
643 */
644void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
645{
646 Q_D(QSGTexture);
647 if ((uint) vwrap != d->verticalWrap) {
648 d->verticalWrap = vwrap;
649 d->wrapChanged = true;
650 }
651}
652
653/*!
654 Returns the vertical wrap mode to be used for this texture.
655 */
656QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
657{
658 return (QSGTexture::WrapMode) d_func()->verticalWrap;
659}
660
661
662/*!
663 Update the texture state to match the filtering, mipmap and wrap options
664 currently set.
665
666 If \a force is true, all properties will be updated regardless of weither
667 they have changed or not.
668 */
669void QSGTexture::updateBindOptions(bool force) // legacy (GL-only)
670{
671#if QT_CONFIG(opengl)
672 Q_D(QSGTexture);
673 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
674 force |= isAtlasTexture();
675
676 if (force || d->filteringChanged) {
677 bool linear = d->filterMode == Linear;
678 GLint minFilter = linear ? GL_LINEAR : GL_NEAREST;
679 GLint magFilter = linear ? GL_LINEAR : GL_NEAREST;
680
681 if (hasMipmaps()) {
682 if (d->mipmapMode == Nearest)
683 minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
684 else if (d->mipmapMode == Linear)
685 minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
686 }
687 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param: minFilter);
688 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param: magFilter);
689 d->filteringChanged = false;
690 }
691
692 if (force || d->anisotropyChanged) {
693 d->anisotropyChanged = false;
694 if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_EXT_texture_filter_anisotropic")))
695 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, param: float(1 << (d->anisotropyLevel)));
696 }
697
698 if (force || d->wrapChanged) {
699#ifndef QT_NO_DEBUG
700 if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat
701 || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat)
702 {
703 bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(feature: QOpenGLFunctions::NPOTTextures);
704 QSize size = textureSize();
705 bool isNpot = !isPowerOfTwo(x: size.width()) || !isPowerOfTwo(x: size.height());
706 if (!npotSupported && isNpot)
707 qWarning(msg: "Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
708 }
709#endif
710 GLenum wrapS = GL_CLAMP_TO_EDGE;
711 if (d->horizontalWrap == Repeat)
712 wrapS = GL_REPEAT;
713 else if (d->horizontalWrap == MirroredRepeat)
714 wrapS = GL_MIRRORED_REPEAT;
715 GLenum wrapT = GL_CLAMP_TO_EDGE;
716 if (d->verticalWrap == Repeat)
717 wrapT = GL_REPEAT;
718 else if (d->verticalWrap == MirroredRepeat)
719 wrapT = GL_MIRRORED_REPEAT;
720 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, param: wrapS);
721 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, param: wrapT);
722 d->wrapChanged = false;
723 }
724#else
725 Q_UNUSED(force)
726#endif
727}
728
729/*!
730 Call this function to enqueue image upload operations to \a
731 resourceUpdates, in case there are any pending ones. When there is no new
732 data (for example, because there was no setImage() since the last call to
733 this function), the function does nothing.
734
735 Materials involving \a rhi textures are expected to call this function from
736 their updateSampledImage() implementation, typically without any conditions.
737
738 \note This function is only used when running the graphics API independent
739 rendering path of the scene graph.
740
741 \warning This function can only be called from the rendering thread.
742
743 \since 5.14
744 */
745void QSGTexture::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
746{
747 Q_D(QSGTexture);
748 d->updateRhiTexture(rhi, resourceUpdates);
749}
750
751/*!
752 \return the platform-specific texture data for this texture.
753
754 \note This is only available when running the graphics API independent
755 rendering path of the scene graph. Use textureId() otherwise.
756
757 Returns an empty result (\c object is null) if there is no available
758 underlying native texture.
759
760 \since 5.15
761 \sa QQuickWindow::createTextureFromNativeObject()
762 */
763QSGTexture::NativeTexture QSGTexture::nativeTexture() const
764{
765 Q_D(const QSGTexture);
766 if (auto *tex = d->rhiTexture()) {
767 auto nativeTexture = tex->nativeTexture();
768 return {.object: nativeTexture.object, .layout: nativeTexture.layout};
769 }
770 return {};
771}
772
773/*!
774 \internal
775 */
776void QSGTexture::setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates)
777{
778 Q_D(QSGTexture);
779 d->workResourceUpdateBatch = resourceUpdates;
780}
781
782bool QSGTexturePrivate::hasDirtySamplerOptions() const
783{
784 return wrapChanged || filteringChanged || anisotropyChanged;
785}
786
787void QSGTexturePrivate::resetDirtySamplerOptions()
788{
789 wrapChanged = filteringChanged = anisotropyChanged = false;
790}
791
792int QSGTexturePrivate::comparisonKey() const
793{
794 // Must be overridden in subclasses but we cannot make this pure virtual
795 // before Qt 6 because the simple QSGTexture ctor must be kept working.
796 Q_Q(const QSGTexture);
797 return q->textureId(); // this is semantically wrong but at least compatible with existing, non-RHI-aware subclasses
798}
799
800/*!
801 \internal
802
803 \return the QRhiTexture for this QSGTexture or null if there is none.
804
805 Unlike textureId(), this function is not expected to create a new
806 QRhiTexture in case there is none. Just return null in that case. The
807 expectation towards the renderer is that a null texture leads to using a
808 transparent, dummy texture instead.
809
810 \note This function is only used when running the graphics API independent
811 rendering path of the scene graph.
812
813 \warning This function can only be called from the rendering thread.
814
815 \since 5.14
816 */
817QRhiTexture *QSGTexturePrivate::rhiTexture() const
818{
819 return nullptr;
820}
821
822void QSGTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
823{
824 Q_UNUSED(rhi);
825 Q_UNUSED(resourceUpdates);
826}
827
828/*!
829 \class QSGDynamicTexture
830 \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
831 such as content that is rendered to FBO's.
832 \inmodule QtQuick
833
834 To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
835 will not update the texture.
836
837 \note All classes with QSG prefix should be used solely on the scene graph's
838 rendering thread. See \l {Scene Graph and Rendering} for more information.
839 */
840
841
842/*!
843 \fn bool QSGDynamicTexture::updateTexture()
844
845 Call this function to explicitly update the dynamic texture.
846
847 The function returns true if the texture was changed as a resul of the update; otherwise
848 returns false.
849
850 \note This function is typically called from QQuickItem::updatePaintNode()
851 or QSGNode::preprocess(), meaning during the \c{synchronization} or the
852 \c{node preprocessing} phases of the scenegraph. Calling it at other times
853 is discouraged and can lead to unexpected behavior.
854 */
855
856/*!
857 \internal
858 */
859QSGDynamicTexture::QSGDynamicTexture(QSGTexturePrivate &dd)
860 : QSGTexture(dd)
861{
862}
863
864QT_END_NAMESPACE
865
866#include "moc_qsgtexture.cpp"
867

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