aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/util/qsgplaintexture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/util/qsgplaintexture.cpp')
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture.cpp260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp
new file mode 100644
index 0000000000..e3ea9ffc17
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgplaintexture.cpp
@@ -0,0 +1,260 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgplaintexture_p.h"
+#include <QtQuick/private/qsgcontext_p.h>
+#include <qmath.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmlglobal_p.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#include <rhi/qrhi.h>
+#include <QtQuick/private/qsgrhisupport_p.h>
+
+#include <qtquick_tracepoints_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGPlainTexture::QSGPlainTexture()
+ : QSGTexture(*(new QSGPlainTexturePrivate(this)))
+ , m_texture(nullptr)
+ , m_has_alpha(false)
+ , m_dirty_texture(false)
+ , m_owns_texture(true)
+ , m_mipmaps_generated(false)
+ , m_retain_image(false)
+ , m_mipmap_warned(false)
+{
+}
+
+QSGPlainTexture::QSGPlainTexture(QSGPlainTexturePrivate &dd)
+ : QSGTexture(dd)
+ , m_texture(nullptr)
+ , m_has_alpha(false)
+ , m_dirty_texture(false)
+ , m_owns_texture(true)
+ , m_mipmaps_generated(false)
+ , m_retain_image(false)
+ , m_mipmap_warned(false)
+{
+}
+
+QSGPlainTexture::~QSGPlainTexture()
+{
+ if (m_texture && m_owns_texture)
+ delete m_texture;
+}
+
+void QSGPlainTexture::setImage(const QImage &image)
+{
+ m_image = image;
+ m_texture_size = image.size();
+ m_has_alpha = image.hasAlphaChannel();
+ m_dirty_texture = true;
+ m_mipmaps_generated = false;
+}
+
+void QSGPlainTexture::setTexture(QRhiTexture *texture) // RHI only
+{
+ if (m_texture && m_owns_texture && m_texture != texture)
+ delete m_texture;
+
+ m_texture = texture;
+ m_dirty_texture = false;
+ m_image = QImage();
+ m_mipmaps_generated = false;
+}
+
+void QSGPlainTexture::setTextureFromNativeTexture(QRhi *rhi,
+ quint64 nativeObjectHandle,
+ int nativeLayoutOrState,
+ uint nativeFormat,
+ const QSize &size,
+ QQuickWindow::CreateTextureOptions options,
+ QQuickWindowPrivate::TextureFromNativeTextureFlags flags)
+{
+ QRhiTexture::Flags texFlags;
+ if (options.testFlag(QQuickWindow::TextureHasMipmaps))
+ texFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+ if (flags.testFlag(QQuickWindowPrivate::NativeTextureIsExternalOES))
+ texFlags |= QRhiTexture::ExternalOES;
+
+ QRhiTexture::Format format = QRhiTexture::RGBA8;
+
+ QRhiTexture::Flags formatFlags;
+ auto rhiFormat = QSGRhiSupport::instance()->toRhiTextureFormat(nativeFormat, &formatFlags);
+ if (rhiFormat != QRhiTexture::UnknownFormat) {
+ format = rhiFormat;
+ texFlags |= formatFlags;
+ }
+
+ QRhiTexture *t = rhi->newTexture(format, size, 1, texFlags);
+
+ // ownership of the native object is never taken
+ t->createFrom({nativeObjectHandle, nativeLayoutOrState});
+
+ setTexture(t);
+}
+
+qint64 QSGPlainTexture::comparisonKey() const
+{
+ if (m_texture)
+ return qint64(m_texture);
+
+ // two textures (and so materials) with not-yet-created texture underneath are never equal
+ return qint64(this);
+}
+
+QRhiTexture *QSGPlainTexture::rhiTexture() const
+{
+ return m_texture;
+}
+
+void QSGPlainTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_D(QSGPlainTexture);
+
+ const bool hasMipMaps = mipmapFiltering() != QSGTexture::None;
+ const bool mipmappingChanged = m_texture && ((hasMipMaps && !m_texture->flags().testFlag(QRhiTexture::MipMapped)) // did not have it before
+ || (!hasMipMaps && m_texture->flags().testFlag(QRhiTexture::MipMapped))); // does not have it anymore
+
+ if (!m_dirty_texture) {
+ if (!m_texture)
+ return;
+ if (m_texture && !mipmappingChanged) {
+ if (hasMipMaps && !m_mipmaps_generated) {
+ resourceUpdates->generateMips(m_texture);
+ m_mipmaps_generated = true;
+ }
+ return;
+ }
+ }
+
+ if (m_image.isNull()) {
+ if (!m_dirty_texture && mipmappingChanged) {
+ // Full Mipmap Panic!
+ if (!m_mipmap_warned) {
+ qWarning("QSGPlainTexture: Mipmap settings changed without having image data available. "
+ "Call setImage() again or enable m_retain_image. "
+ "Falling back to previous mipmap filtering mode.");
+ m_mipmap_warned = true;
+ }
+ // leave the texture valid and rather ignore the mipmap mode change attempt
+ setMipmapFiltering(d->m_last_mipmap_filter);
+ return;
+ }
+
+ if (m_texture && m_owns_texture)
+ delete m_texture;
+
+ m_texture = nullptr;
+ m_texture_size = QSize();
+ m_has_alpha = false;
+
+ m_dirty_texture = false;
+ return;
+ }
+
+ m_dirty_texture = false;
+
+ QImage tmp;
+ bool bgra = false;
+ bool needsConvert = false;
+ if (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (rhi->isTextureFormatSupported(QRhiTexture::BGRA8)) {
+ tmp = m_image;
+ bgra = true;
+ } else {
+ needsConvert = true;
+ }
+#else
+ needsConvert = true;
+#endif
+ } else if (m_image.format() == QImage::Format_RGBX8888 || m_image.format() == QImage::Format_RGBA8888_Premultiplied) {
+ tmp = m_image;
+ } else {
+ needsConvert = true;
+ }
+
+ if (needsConvert)
+ tmp = m_image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+
+ // Downscale the texture to fit inside the max texture limit if it is too big.
+ // It would be better if the image was already downscaled to the right size,
+ // but this information is not always available at that time, so as a last
+ // resort we can do it here. Texture coordinates are normalized, so it
+ // won't cause any problems and actual texture sizes will be written
+ // based on QSGTexture::textureSize which is updated after this, so that
+ // should be ok.
+ const int max = rhi->resourceLimit(QRhi::TextureSizeMax);
+ if (tmp.width() > max || tmp.height() > max) {
+ tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ m_texture_size = tmp.size();
+ }
+
+ if ((mipmapFiltering() != QSGTexture::None
+ || horizontalWrapMode() != QSGTexture::ClampToEdge
+ || verticalWrapMode() != QSGTexture::ClampToEdge)
+ && !rhi->isFeatureSupported(QRhi::NPOTTextureRepeat))
+ {
+ const int w = qNextPowerOfTwo(tmp.width() - 1);
+ const int h = qNextPowerOfTwo(tmp.height() - 1);
+ if (tmp.width() != w || tmp.height() != h) {
+ tmp = tmp.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ m_texture_size = tmp.size();
+ }
+ }
+
+ bool needsRebuild = false;
+
+ if (m_texture && m_texture->pixelSize() != m_texture_size) {
+ m_texture->setPixelSize(m_texture_size);
+ needsRebuild = true;
+ }
+
+ if (mipmappingChanged) {
+ QRhiTexture::Flags f = m_texture->flags();
+ f.setFlag(QRhiTexture::MipMapped, hasMipMaps);
+ f.setFlag(QRhiTexture::UsedWithGenerateMips, hasMipMaps);
+ m_texture->setFlags(f);
+ needsRebuild = true;
+ }
+
+ if (!m_texture) {
+ QRhiTexture::Flags f;
+ if (hasMipMaps)
+ f |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+
+ m_texture = rhi->newTexture(bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8, m_texture_size, 1, f);
+ needsRebuild = true;
+ }
+
+ if (needsRebuild) {
+ if (!m_texture->create()) {
+ qWarning("Failed to build texture for QSGPlainTexture (size %dx%d)",
+ m_texture_size.width(), m_texture_size.height());
+ return;
+ }
+ }
+
+ if (tmp.width() * 4 != tmp.bytesPerLine())
+ tmp = tmp.copy();
+
+ resourceUpdates->uploadTexture(m_texture, tmp);
+
+ if (hasMipMaps) {
+ resourceUpdates->generateMips(m_texture);
+ m_mipmaps_generated = true;
+ }
+
+ d->m_last_mipmap_filter = mipmapFiltering();
+ m_texture_rect = QRectF(0, 0, 1, 1);
+
+ if (!m_retain_image)
+ m_image = QImage();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsgplaintexture_p.cpp"