1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsgplaintexture_p.h"
5#include <QtQuick/private/qsgcontext_p.h>
6#include <qmath.h>
7#include <private/qquickprofiler_p.h>
8#include <private/qqmlglobal_p.h>
9#include <QtGui/qguiapplication.h>
10#include <QtGui/qpa/qplatformnativeinterface.h>
11#include <rhi/qrhi.h>
12#include <QtQuick/private/qsgrhisupport_p.h>
13
14#include <qtquick_tracepoints_p.h>
15
16QT_BEGIN_NAMESPACE
17
18QSGPlainTexture::QSGPlainTexture()
19 : QSGTexture(*(new QSGPlainTexturePrivate(this)))
20 , m_texture(nullptr)
21 , m_has_alpha(false)
22 , m_dirty_texture(false)
23 , m_owns_texture(true)
24 , m_mipmaps_generated(false)
25 , m_retain_image(false)
26 , m_mipmap_warned(false)
27{
28}
29
30QSGPlainTexture::QSGPlainTexture(QSGPlainTexturePrivate &dd)
31 : QSGTexture(dd)
32 , m_texture(nullptr)
33 , m_has_alpha(false)
34 , m_dirty_texture(false)
35 , m_owns_texture(true)
36 , m_mipmaps_generated(false)
37 , m_retain_image(false)
38 , m_mipmap_warned(false)
39{
40}
41
42QSGPlainTexture::~QSGPlainTexture()
43{
44 if (m_texture && m_owns_texture)
45 delete m_texture;
46}
47
48void QSGPlainTexture::setImage(const QImage &image)
49{
50 m_image = image;
51 m_texture_size = image.size();
52 m_has_alpha = image.hasAlphaChannel();
53 m_dirty_texture = true;
54 m_mipmaps_generated = false;
55}
56
57void QSGPlainTexture::setTexture(QRhiTexture *texture) // RHI only
58{
59 if (m_texture && m_owns_texture && m_texture != texture)
60 delete m_texture;
61
62 m_texture = texture;
63 m_dirty_texture = false;
64 m_image = QImage();
65 m_mipmaps_generated = false;
66}
67
68void QSGPlainTexture::setTextureFromNativeTexture(QRhi *rhi,
69 quint64 nativeObjectHandle,
70 int nativeLayoutOrState,
71 uint nativeFormat,
72 const QSize &size,
73 QQuickWindow::CreateTextureOptions options,
74 QQuickWindowPrivate::TextureFromNativeTextureFlags flags)
75{
76 QRhiTexture::Flags texFlags;
77 if (options.testFlag(flag: QQuickWindow::TextureHasMipmaps))
78 texFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
79 if (flags.testFlag(flag: QQuickWindowPrivate::NativeTextureIsExternalOES))
80 texFlags |= QRhiTexture::ExternalOES;
81
82 QRhiTexture::Format format = QRhiTexture::RGBA8;
83
84 QRhiTexture::Flags formatFlags;
85 auto rhiFormat = QSGRhiSupport::instance()->toRhiTextureFormat(nativeFormat, flags: &formatFlags);
86 if (rhiFormat != QRhiTexture::UnknownFormat) {
87 format = rhiFormat;
88 texFlags |= formatFlags;
89 }
90
91 QRhiTexture *t = rhi->newTexture(format, pixelSize: size, sampleCount: 1, flags: texFlags);
92
93 // ownership of the native object is never taken
94 t->createFrom(src: {.object: nativeObjectHandle, .layout: nativeLayoutOrState});
95
96 setTexture(t);
97}
98
99qint64 QSGPlainTexture::comparisonKey() const
100{
101 if (m_texture)
102 return qint64(m_texture);
103
104 // two textures (and so materials) with not-yet-created texture underneath are never equal
105 return qint64(this);
106}
107
108QRhiTexture *QSGPlainTexture::rhiTexture() const
109{
110 return m_texture;
111}
112
113void QSGPlainTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
114{
115 Q_D(QSGPlainTexture);
116
117 const bool hasMipMaps = mipmapFiltering() != QSGTexture::None;
118 const bool mipmappingChanged = m_texture && ((hasMipMaps && !m_texture->flags().testFlag(flag: QRhiTexture::MipMapped)) // did not have it before
119 || (!hasMipMaps && m_texture->flags().testFlag(flag: QRhiTexture::MipMapped))); // does not have it anymore
120
121 if (!m_dirty_texture) {
122 if (!m_texture)
123 return;
124 if (m_texture && !mipmappingChanged) {
125 if (hasMipMaps && !m_mipmaps_generated) {
126 resourceUpdates->generateMips(tex: m_texture);
127 m_mipmaps_generated = true;
128 }
129 return;
130 }
131 }
132
133 if (m_image.isNull()) {
134 if (!m_dirty_texture && mipmappingChanged) {
135 // Full Mipmap Panic!
136 if (!m_mipmap_warned) {
137 qWarning(msg: "QSGPlainTexture: Mipmap settings changed without having image data available. "
138 "Call setImage() again or enable m_retain_image. "
139 "Falling back to previous mipmap filtering mode.");
140 m_mipmap_warned = true;
141 }
142 // leave the texture valid and rather ignore the mipmap mode change attempt
143 setMipmapFiltering(d->m_last_mipmap_filter);
144 return;
145 }
146
147 if (m_texture && m_owns_texture)
148 delete m_texture;
149
150 m_texture = nullptr;
151 m_texture_size = QSize();
152 m_has_alpha = false;
153
154 m_dirty_texture = false;
155 return;
156 }
157
158 m_dirty_texture = false;
159
160 QImage tmp;
161 bool bgra = false;
162 bool needsConvert = false;
163 if (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied) {
164#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
165 if (rhi->isTextureFormatSupported(format: QRhiTexture::BGRA8)) {
166 tmp = m_image;
167 bgra = true;
168 } else {
169 needsConvert = true;
170 }
171#else
172 needsConvert = true;
173#endif
174 } else if (m_image.format() == QImage::Format_RGBX8888 || m_image.format() == QImage::Format_RGBA8888_Premultiplied) {
175 tmp = m_image;
176 } else {
177 needsConvert = true;
178 }
179
180 if (needsConvert)
181 tmp = m_image.convertToFormat(f: QImage::Format_RGBA8888_Premultiplied);
182
183 // Downscale the texture to fit inside the max texture limit if it is too big.
184 // It would be better if the image was already downscaled to the right size,
185 // but this information is not always available at that time, so as a last
186 // resort we can do it here. Texture coordinates are normalized, so it
187 // won't cause any problems and actual texture sizes will be written
188 // based on QSGTexture::textureSize which is updated after this, so that
189 // should be ok.
190 const int max = rhi->resourceLimit(limit: QRhi::TextureSizeMax);
191 if (tmp.width() > max || tmp.height() > max) {
192 tmp = tmp.scaled(w: qMin(a: max, b: tmp.width()), h: qMin(a: max, b: tmp.height()), aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation);
193 m_texture_size = tmp.size();
194 }
195
196 if ((mipmapFiltering() != QSGTexture::None
197 || horizontalWrapMode() != QSGTexture::ClampToEdge
198 || verticalWrapMode() != QSGTexture::ClampToEdge)
199 && !rhi->isFeatureSupported(feature: QRhi::NPOTTextureRepeat))
200 {
201 const int w = qNextPowerOfTwo(v: tmp.width() - 1);
202 const int h = qNextPowerOfTwo(v: tmp.height() - 1);
203 if (tmp.width() != w || tmp.height() != h) {
204 tmp = tmp.scaled(w, h, aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation);
205 m_texture_size = tmp.size();
206 }
207 }
208
209 bool needsRebuild = false;
210
211 if (m_texture && m_texture->pixelSize() != m_texture_size) {
212 m_texture->setPixelSize(m_texture_size);
213 needsRebuild = true;
214 }
215
216 if (mipmappingChanged) {
217 QRhiTexture::Flags f = m_texture->flags();
218 f.setFlag(flag: QRhiTexture::MipMapped, on: hasMipMaps);
219 f.setFlag(flag: QRhiTexture::UsedWithGenerateMips, on: hasMipMaps);
220 m_texture->setFlags(f);
221 needsRebuild = true;
222 }
223
224 if (!m_texture) {
225 QRhiTexture::Flags f;
226 if (hasMipMaps)
227 f |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
228
229 m_texture = rhi->newTexture(format: bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8, pixelSize: m_texture_size, sampleCount: 1, flags: f);
230 needsRebuild = true;
231 }
232
233 if (needsRebuild) {
234 if (!m_texture->create()) {
235 qWarning(msg: "Failed to build texture for QSGPlainTexture (size %dx%d)",
236 m_texture_size.width(), m_texture_size.height());
237 return;
238 }
239 }
240
241 if (tmp.width() * 4 != tmp.bytesPerLine())
242 tmp = tmp.copy();
243
244 resourceUpdates->uploadTexture(tex: m_texture, image: tmp);
245
246 if (hasMipMaps) {
247 resourceUpdates->generateMips(tex: m_texture);
248 m_mipmaps_generated = true;
249 }
250
251 d->m_last_mipmap_filter = mipmapFiltering();
252 m_texture_rect = QRectF(0, 0, 1, 1);
253
254 if (!m_retain_image)
255 m_image = QImage();
256}
257
258QT_END_NAMESPACE
259
260#include "moc_qsgplaintexture_p.cpp"
261

source code of qtdeclarative/src/quick/scenegraph/util/qsgplaintexture.cpp