From 341ab7708049b1a3f559b76f16393e688951a938 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 23 Apr 2019 09:40:59 +0200 Subject: Add the graphics api independent scenegraph port Opt in via environment variables: QSG_RHI=1 -> enable using QRhi instead of GL QSG_RHI_BACKEND -> set to vulkan, metal, d3d11, gl to override the default (the default is d3d11 on Windows, metal on Mac, gl elsewhere) Or force a given rhi backend via the existing QQuickWindow::setSceneGraphBackend(). Otherwise the default behavior is the same as before, the rhi code path is never active by default. -no-opengl builds are supported in the sense that they work and default to the software backend. However, the rhi code path cannot currently be used in such builds, even though QRhi from qtbase is fully functional with Vulkan, D3D, or Metal even when qtbase was configured with -no-opengl. This cannot be utilized by Quick atm due to OpenGL usage being all over the place in the sources corresponding to the default backend, and those host the rhi code path as well. This will be cleaned up hopefully in Qt 6, with the removal all direct OpenGL usage. Other env.vars.: QSG_RHI_DEBUG_LAYER=1 -> enable D3D debug or Vulkan validation layer (assuming the system is set up for this) QSG_RHI_SHADEREFFECT_DEBUG=1 -> print stuff from ShaderEffect QSG_SAMPLES=1,2,4,... -> MSAA sample count (but QSurfaceFormat works too) QT_D3D_ADAPTER_INDEX=0,1,... -> D3D adapter index QT_VK_PHYSICAL_DEVICE_INDEX=0,1,... -> Vulkan physical device index QSG_RHI_UINT32_INDEX=1 -> always use uint index data (both merged/unmerged, convert when needed - with some rhi backends this is implicit) QSG_RENDER_LOOP -> to override the render loop as usual. The default with RHI is threaded for Metal, threaded for Vulkan on Windows, basic for Vulkan on Linux and Android (to be checked later), while the existing rules apply for OpenGL. Not supported when running with QRhi: - particles - compressed atlases (though this is transparent to the apps) - QSGRenderNode - QQuickRenderControl - QQuickFramebufferObject - certain QQuickWindow functionality that depends directly on OpenGL - anisotropic filtering for textures - native text may lack some gamma correction - QSGEngine applicability unclear - some QML profiler logs may be incorrect or irrelevant Change-Id: I7822e99ad79e342e4166275da6e9e66498d76521 Reviewed-by: Lars Knoll --- src/quick/scenegraph/coreapi/qsgtexture.cpp | 810 ++++++++++++++++++++++++++++ 1 file changed, 810 insertions(+) create mode 100644 src/quick/scenegraph/coreapi/qsgtexture.cpp (limited to 'src/quick/scenegraph/coreapi/qsgtexture.cpp') diff --git a/src/quick/scenegraph/coreapi/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp new file mode 100644 index 0000000000..cfd0cb9f06 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp @@ -0,0 +1,810 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgtexture_p.h" +#if QT_CONFIG(opengl) +# include +# include +#endif +#include +#include +#include + +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__) +#define CAN_BACKTRACE_EXECINFO +#endif + +#if defined(Q_OS_MAC) +#define CAN_BACKTRACE_EXECINFO +#endif + +#if defined(QT_NO_DEBUG) +#undef CAN_BACKTRACE_EXECINFO +#endif + +#if defined(CAN_BACKTRACE_EXECINFO) +#include +#include +#endif + +#ifndef QT_NO_DEBUG +static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); +#endif + +QT_BEGIN_NAMESPACE + +bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW +{ + return a.filtering == b.filtering + && a.mipmapFiltering == b.mipmapFiltering + && a.horizontalWrap == b.horizontalWrap + && a.verticalWrap == b.verticalWrap + && a.anisotropylevel == b.anisotropylevel; +} + +bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +uint qHash(const QSGSamplerDescription &s, uint seed) Q_DECL_NOTHROW +{ + const int f = s.filtering; + const int m = s.mipmapFiltering; + const int w = s.horizontalWrap; + const int a = s.anisotropylevel; + return (((f & 7) << 24) | ((m & 7) << 16) | ((w & 7) << 8) | (a & 7)) ^ seed; +} + +QSGSamplerDescription QSGSamplerDescription::fromTexture(QSGTexture *t) +{ + QSGSamplerDescription s; + s.filtering = t->filtering(); + s.mipmapFiltering = t->mipmapFiltering(); + s.horizontalWrap = t->horizontalWrapMode(); + s.verticalWrap = t->verticalWrapMode(); + s.anisotropylevel = t->anisotropyLevel(); + return s; +} + +#if QT_CONFIG(opengl) +#ifndef QT_NO_DEBUG +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} +#endif +#endif + +QSGTexturePrivate::QSGTexturePrivate() + : wrapChanged(false) + , filteringChanged(false) + , anisotropyChanged(false) + , horizontalWrap(QSGTexture::ClampToEdge) + , verticalWrap(QSGTexture::ClampToEdge) + , mipmapMode(QSGTexture::None) + , filterMode(QSGTexture::Nearest) + , anisotropyLevel(QSGTexture::AnisotropyNone) +{ +} + +#ifndef QT_NO_DEBUG + +static int qt_debug_texture_count = 0; + +#if (defined(Q_OS_LINUX) || defined (Q_OS_MAC)) && !defined(Q_OS_ANDROID) +DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE) + +#define BACKTRACE_SIZE 20 +class SGTextureTraceItem +{ +public: + void *backTrace[BACKTRACE_SIZE]; + size_t backTraceSize; +}; + +static QHash qt_debug_allocated_textures; +#endif + +inline static void qt_debug_print_texture_count() +{ + qDebug("Number of leaked textures: %i", qt_debug_texture_count); + qt_debug_texture_count = -1; + +#if defined(CAN_BACKTRACE_EXECINFO) + if (qmlDebugLeakBacktrace()) { + while (!qt_debug_allocated_textures.isEmpty()) { + QHash::Iterator it = qt_debug_allocated_textures.begin(); + QSGTexture* texture = it.key(); + SGTextureTraceItem* item = it.value(); + + qt_debug_allocated_textures.erase(it); + + qDebug() << "------"; + qDebug() << "Leaked" << texture << "backtrace:"; + + char** symbols = backtrace_symbols(item->backTrace, item->backTraceSize); + + if (symbols) { + for (int i=0; i<(int) item->backTraceSize; i++) + qDebug("Backtrace <%02d>: %s", i, symbols[i]); + free(symbols); + } + + qDebug() << "------"; + + delete item; + } + } +#endif +} + +inline static void qt_debug_add_texture(QSGTexture* texture) +{ +#if defined(CAN_BACKTRACE_EXECINFO) + if (qmlDebugLeakBacktrace()) { + SGTextureTraceItem* item = new SGTextureTraceItem; + item->backTraceSize = backtrace(item->backTrace, BACKTRACE_SIZE); + qt_debug_allocated_textures.insert(texture, item); + } +#else + Q_UNUSED(texture); +#endif // Q_OS_LINUX + + ++qt_debug_texture_count; + + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_debug_print_texture_count); + atexit_registered = true; + } +} + +static void qt_debug_remove_texture(QSGTexture* texture) +{ +#if defined(CAN_BACKTRACE_EXECINFO) + if (qmlDebugLeakBacktrace()) { + SGTextureTraceItem* item = qt_debug_allocated_textures.value(texture, 0); + if (item) { + qt_debug_allocated_textures.remove(texture); + delete item; + } + } +#else + Q_UNUSED(texture) +#endif + + --qt_debug_texture_count; + + if (qt_debug_texture_count < 0) + qDebug("Texture destroyed after qt_debug_print_texture_count() was called."); +} + +#endif // QT_NO_DEBUG + +/*! + \class QSGTexture + + \inmodule QtQuick + + \brief The QSGTexture class is a baseclass for textures used in + the scene graph. + + + Users can freely implement their own texture classes to support + arbitrary input textures, such as YUV video frames or 8 bit alpha + masks. The scene graph backend provides a default implementation + of normal color textures. As the implementation of these may be + hardware specific, they are constructed via the factory + function QQuickWindow::createTextureFromImage(). + + The texture is a wrapper around an OpenGL texture, which texture + id is given by textureId() and which size in pixels is given by + textureSize(). hasAlphaChannel() reports if the texture contains + opacity values and hasMipmaps() reports if the texture contains + mipmap levels. + + To use a texture, call the bind() function. The texture parameters + specifying how the texture is bound, can be specified with + setMipmapFiltering(), setFiltering(), setHorizontalWrapMode() and + setVerticalWrapMode(). The texture will internally try to store + these values to minimize the OpenGL state changes when the texture + is bound. + + \section1 Texture Atlasses + + Some scene graph backends use texture atlasses, grouping multiple + small textures into one large texture. If this is the case, the + function isAtlasTexture() will return true. Atlasses are used to + aid the rendering algorithm to do better sorting which increases + performance. The location of the texture inside the atlas is + given with the normalizedTextureSubRect() function. + + If the texture is used in such a way that atlas is not preferable, + the function removedFromAtlas() can be used to extract a + non-atlassed copy. + + \note All classes with QSG prefix should be used solely on the scene graph's + rendering thread. See \l {Scene Graph and Rendering} for more information. + + \sa {Scene Graph - Rendering FBOs}, {Scene Graph - Rendering FBOs in a thread} + */ + +/*! + \enum QSGTexture::WrapMode + + Specifies how the texture should treat texture coordinates. + + \value Repeat Only the fractional part of the texture coordinate is + used, causing values above 1 and below 0 to repeat. + + \value ClampToEdge Values above 1 are clamped to 1 and values + below 0 are clamped to 0. + + \value MirroredRepeat When the texture coordinate is even, only the + fractional part is used. When odd, the texture coordinate is set to + \c{1 - fractional part}. This value has been introduced in Qt 5.10. + */ + +/*! + \enum QSGTexture::Filtering + + Specifies how sampling of texels should filter when texture + coordinates are not pixel aligned. + + \value None No filtering should occur. This value is only used + together with setMipmapFiltering(). + + \value Nearest Sampling returns the nearest texel. + + \value Linear Sampling returns a linear interpolation of the + neighboring texels. +*/ + +/*! + \enum QSGTexture::AnisotropyLevel + + Specifies the anisotropic filtering level to be used when + the texture is not screen aligned. + + \value AnisotropyNone No anisotropic filtering. + + \value Anisotropy2x 2x anisotropic filtering. + + \value Anisotropy4x 4x anisotropic filtering. + + \value Anisotropy8x 8x anisotropic filtering. + + \value Anisotropy16x 16x anisotropic filtering. + + \since 5.9 +*/ + +#ifndef QT_NO_DEBUG +Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure(); +#endif + +#ifndef QT_NO_DEBUG +Q_GLOBAL_STATIC(QSet, qsg_valid_texture_set) +Q_GLOBAL_STATIC(QMutex, qsg_valid_texture_mutex) + +bool qsg_safeguard_texture(QSGTexture *texture) +{ +#if QT_CONFIG(opengl) + QMutexLocker locker(qsg_valid_texture_mutex()); + if (!qsg_valid_texture_set()->contains(texture)) { + qWarning() << "Invalid texture accessed:" << (void *) texture; + qsg_set_material_failure(); + QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, 0); + return false; + } +#else + Q_UNUSED(texture) +#endif + return true; +} +#endif + +/*! + Constructs the QSGTexture base class. + */ +QSGTexture::QSGTexture() + : QObject(*(new QSGTexturePrivate)) +{ +#ifndef QT_NO_DEBUG + if (qsg_leak_check) + qt_debug_add_texture(this); + + QMutexLocker locker(qsg_valid_texture_mutex()); + qsg_valid_texture_set()->insert(this); +#endif +} + +/*! + \internal + */ +QSGTexture::QSGTexture(QSGTexturePrivate &dd) + : QObject(dd) +{ +#ifndef QT_NO_DEBUG + if (qsg_leak_check) + qt_debug_add_texture(this); + + QMutexLocker locker(qsg_valid_texture_mutex()); + qsg_valid_texture_set()->insert(this); +#endif +} + +/*! + Destroys the QSGTexture. + */ +QSGTexture::~QSGTexture() +{ +#ifndef QT_NO_DEBUG + if (qsg_leak_check) + qt_debug_remove_texture(this); + + QMutexLocker locker(qsg_valid_texture_mutex()); + qsg_valid_texture_set()->remove(this); +#endif +} + +/*! + \fn void QSGTexture::bind() + + Call this function to bind this texture to the current texture + target. + + Binding a texture may also include uploading the texture data from + a previously set QImage. + + \warning This function can only be called from the rendering thread. + */ + +/*! + \fn QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const + + Returns \a rect converted to normalized coordinates. + + \sa normalizedTextureSubRect() + */ + +/*! + This function returns a copy of the current texture which is removed + from its atlas. + + The current texture remains unchanged, so texture coordinates do not + need to be updated. + + Removing a texture from an atlas is primarily useful when passing + it to a shader that operates on the texture coordinates 0-1 instead + of the texture subrect inside the atlas. + + If the texture is not part of a texture atlas, this function returns 0. + + Implementations of this function are recommended to return the same instance + for multiple calls to limit memory usage. + + \warning This function can only be called from the rendering thread. + */ + +QSGTexture *QSGTexture::removedFromAtlas() const +{ + Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture"); + return nullptr; +} + +/*! + Returns weither this texture is part of an atlas or not. + + The default implementation returns false. + */ +bool QSGTexture::isAtlasTexture() const +{ + return false; +} + +/*! + \fn int QSGTexture::textureId() const + + Returns the OpenGL texture id for this texture. + + The default value is 0, indicating that it is an invalid texture id. + + The function should at all times return the correct texture id. + + \warning This function can only be called from the rendering thread. + */ + +/*! + Returns a key suitable for comparing textures. Typically used in + QSGMaterial::compare() implementations. + + Just comparing QSGTexture pointers is not always sufficient because two + QSGTexture instances that refer to the same native texture object + underneath should also be considered equal. Hence this function. + + \note Unlike textureId(), implementations of this function are not expected + to and should not create any graphics resources (so texture objects) in + case there is none yet. + + A QSGTexture that does not have a native texture object underneath is + typically not equal to any other QSGTexture. There are exceptions to this, + in particular when atlasing is used (where multiple textures share the same + atlas texture under the hood), that is then up to the subclass + implementations to deal with as appropriate. + + \warning This function can only be called from the rendering thread. + + \since 5.14 + */ +int QSGTexture::comparisonKey() const +{ + Q_D(const QSGTexture); + return d->comparisonKey(); +} + +/*! + \fn QSize QSGTexture::textureSize() const + + Returns the size of the texture. + */ + +/*! + Returns the rectangle inside textureSize() that this texture + represents in normalized coordinates. + + The default implementation returns a rect at position (0, 0) with + width and height of 1. + */ +QRectF QSGTexture::normalizedTextureSubRect() const +{ + return QRectF(0, 0, 1, 1); +} + +/*! + \fn bool QSGTexture::hasAlphaChannel() const + + Returns true if the texture data contains an alpha channel. + */ + +/*! + \fn bool QSGTexture::hasMipmaps() const + + Returns true if the texture data contains mipmap levels. + */ + + +/*! + Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter. + + Setting the mipmap filtering has no effect it the texture does not have mipmaps. + + \sa hasMipmaps() + */ +void QSGTexture::setMipmapFiltering(Filtering filter) +{ + Q_D(QSGTexture); + if (d->mipmapMode != (uint) filter) { + d->mipmapMode = filter; + d->filteringChanged = true; + } +} + +/*! + Returns whether mipmapping should be used when sampling from this texture. + */ +QSGTexture::Filtering QSGTexture::mipmapFiltering() const +{ + return (QSGTexture::Filtering) d_func()->mipmapMode; +} + + +/*! + Sets the sampling mode to be used for the upcoming bind() call to \a filter. + */ +void QSGTexture::setFiltering(QSGTexture::Filtering filter) +{ + Q_D(QSGTexture); + if (d->filterMode != (uint) filter) { + d->filterMode = filter; + d->filteringChanged = true; + } +} + +/*! + Returns the sampling mode to be used for this texture. + */ +QSGTexture::Filtering QSGTexture::filtering() const +{ + return (QSGTexture::Filtering) d_func()->filterMode; +} + +/*! + Sets the level of anisotropic filtering to be used for the upcoming bind() call to \a level. + The default value is QSGTexture::AnisotropyNone, which means no anisotropic filtering is enabled. + + \since 5.9 + */ +void QSGTexture::setAnisotropyLevel(AnisotropyLevel level) +{ + Q_D(QSGTexture); + if (d->anisotropyLevel != (uint) level) { + d->anisotropyLevel = level; + d->anisotropyChanged = true; + } +} + +/*! + Returns the anisotropy level in use for filtering this texture. + + \since 5.9 + */ +QSGTexture::AnisotropyLevel QSGTexture::anisotropyLevel() const +{ + return (QSGTexture::AnisotropyLevel) d_func()->anisotropyLevel; +} + + + +/*! + Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap + */ + +void QSGTexture::setHorizontalWrapMode(WrapMode hwrap) +{ + Q_D(QSGTexture); + if ((uint) hwrap != d->horizontalWrap) { + d->horizontalWrap = hwrap; + d->wrapChanged = true; + } +} + +/*! + Returns the horizontal wrap mode to be used for this texture. + */ +QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const +{ + return (QSGTexture::WrapMode) d_func()->horizontalWrap; +} + + + +/*! + Sets the vertical wrap mode to be used for the upcoming bind() call to \a vwrap + */ +void QSGTexture::setVerticalWrapMode(WrapMode vwrap) +{ + Q_D(QSGTexture); + if ((uint) vwrap != d->verticalWrap) { + d->verticalWrap = vwrap; + d->wrapChanged = true; + } +} + +/*! + Returns the vertical wrap mode to be used for this texture. + */ +QSGTexture::WrapMode QSGTexture::verticalWrapMode() const +{ + return (QSGTexture::WrapMode) d_func()->verticalWrap; +} + + +/*! + Update the texture state to match the filtering, mipmap and wrap options + currently set. + + If \a force is true, all properties will be updated regardless of weither + they have changed or not. + */ +void QSGTexture::updateBindOptions(bool force) // legacy (GL-only) +{ +#if QT_CONFIG(opengl) + Q_D(QSGTexture); + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + force |= isAtlasTexture(); + + if (force || d->filteringChanged) { + bool linear = d->filterMode == Linear; + GLint minFilter = linear ? GL_LINEAR : GL_NEAREST; + GLint magFilter = linear ? GL_LINEAR : GL_NEAREST; + + if (hasMipmaps()) { + if (d->mipmapMode == Nearest) + minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST; + else if (d->mipmapMode == Linear) + minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR; + } + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); + d->filteringChanged = false; + } + + if (force || d->anisotropyChanged) { + d->anisotropyChanged = false; + if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_EXT_texture_filter_anisotropic"))) + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, float(1 << (d->anisotropyLevel))); + } + + if (force || d->wrapChanged) { +#ifndef QT_NO_DEBUG + if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat + || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat) + { + bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + QSize size = textureSize(); + bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + if (!npotSupported && isNpot) + qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures."); + } +#endif + GLenum wrapS = GL_CLAMP_TO_EDGE; + if (d->horizontalWrap == Repeat) + wrapS = GL_REPEAT; + else if (d->horizontalWrap == MirroredRepeat) + wrapS = GL_MIRRORED_REPEAT; + GLenum wrapT = GL_CLAMP_TO_EDGE; + if (d->verticalWrap == Repeat) + wrapT = GL_REPEAT; + else if (d->verticalWrap == MirroredRepeat) + wrapT = GL_MIRRORED_REPEAT; + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); + d->wrapChanged = false; + } +#else + Q_UNUSED(force) +#endif +} + +/*! + \return the QRhiTexture for this QSGTexture or null if there is none. + + Unlike textureId(), this function is not expected to create a new + QRhiTexture in case there is none. Just return null in that case. The + expectation towards the renderer is that a null texture leads to using a + transparent, dummy texture instead. + + \note This function is only used when running the graphics API independent + rendering path of the scene graph. + + \warning This function can only be called from the rendering thread. + + \since 5.14 + */ +QRhiTexture *QSGTexture::rhiTexture() const +{ + Q_D(const QSGTexture); + return d->rhiTexture(); +} + +/*! + Call this function to enqueue image upload operations to \a + resourceUpdates, in case there are any pending ones. When there is no new + data (for example, because there was no setImage() since the last call to + this function), the function does nothing. + + Materials involving textures are expected to call this function from their + updateSampledImage() implementation, typically without any conditions. + + \note This function is only used when running the graphics API independent + rendering path of the scene graph. + + \warning This function can only be called from the rendering thread. + + \since 5.14 + */ +void QSGTexture::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_D(QSGTexture); + d->updateRhiTexture(rhi, resourceUpdates); +} + +void QSGTexture::setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_D(QSGTexture); + d->workResourceUpdateBatch = resourceUpdates; +} + +bool QSGTexturePrivate::hasDirtySamplerOptions() const +{ + return wrapChanged || filteringChanged || anisotropyChanged; +} + +void QSGTexturePrivate::resetDirtySamplerOptions() +{ + wrapChanged = filteringChanged = anisotropyChanged = false; +} + +int QSGTexturePrivate::comparisonKey() const +{ + // Must be overridden in subclasses but we cannot make this pure virtual + // before Qt 6 because the simple QSGTexture ctor must be kept working. + Q_Q(const QSGTexture); + return q->textureId(); // this is semantically wrong but at least compatible with existing, non-RHI-aware subclasses +} + +QRhiTexture *QSGTexturePrivate::rhiTexture() const +{ + return nullptr; +} + +void QSGTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_UNUSED(rhi); + Q_UNUSED(resourceUpdates); +} + +/*! + \class QSGDynamicTexture + \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures, + such as content that is rendered to FBO's. + \inmodule QtQuick + + To update the content of the texture, call updateTexture() explicitly. Simply calling bind() + will not update the texture. + + \note All classes with QSG prefix should be used solely on the scene graph's + rendering thread. See \l {Scene Graph and Rendering} for more information. + */ + + +/*! + \fn bool QSGDynamicTexture::updateTexture() + + Call this function to explicitly update the dynamic texture. Calling bind() will bind + the content that was previously updated. + + The function returns true if the texture was changed as a resul of the update; otherwise + returns false. + */ + +/*! + \internal + */ +QSGDynamicTexture::QSGDynamicTexture(QSGTexturePrivate &dd) + : QSGTexture(dd) +{ +} + +QT_END_NAMESPACE + +#include "moc_qsgtexture.cpp" -- cgit v1.2.3