Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgplaintexture.cpp
Go to the documentation of this file.
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
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
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(QQuickWindow::TextureHasMipmaps))
78 texFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
79 if (flags.testFlag(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, &formatFlags);
86 if (rhiFormat != QRhiTexture::UnknownFormat) {
87 format = rhiFormat;
88 texFlags |= formatFlags;
89 }
90
91 QRhiTexture *t = rhi->newTexture(format, size, 1, texFlags);
92
93 // ownership of the native object is never taken
94 t->createFrom({nativeObjectHandle, 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(QRhiTexture::MipMapped)) // did not have it before
119 || (!hasMipMaps && m_texture->flags().testFlag(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(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("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 fp16 = false;
163 bool fp32 = false;
164 bool needsConvert8 = false;
165 bool needsConvertFP16 = false;
166 bool needsConvertFP32 = false;
167
168 if (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied) {
169#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
170 if (rhi->isTextureFormatSupported(QRhiTexture::BGRA8)) {
171 tmp = m_image;
172 bgra = true;
173 } else {
174 needsConvert8 = true;
175 }
176#else
177 needsConvert8 = true;
178#endif
179 } else if (m_image.format() == QImage::Format_RGBX16FPx4 || m_image.format() == QImage::Format_RGBA16FPx4_Premultiplied) {
180#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
181 if (rhi->isTextureFormatSupported(QRhiTexture::RGBA16F)) {
182 tmp = m_image;
183 fp16 = true;
184 } else {
185 needsConvert8 = true;
186 }
187#else
188 needsConvert8 = true;
189#endif
190 } else if (m_image.format() == QImage::Format_RGBX32FPx4 || m_image.format() == QImage::Format_RGBA32FPx4_Premultiplied) {
191#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
192 if (rhi->isTextureFormatSupported(QRhiTexture::RGBA32F)) {
193 tmp = m_image;
194 fp32 = true;
195 } else if (rhi->isTextureFormatSupported(QRhiTexture::RGBA16F)) {
196 tmp = m_image;
197 fp16 = true;
198 } else {
199 needsConvert8 = true;
200 }
201#else
202 needsConvert8 = true;
203#endif
204 } else if (m_image.format() == QImage::Format_RGBX8888 || m_image.format() == QImage::Format_RGBA8888_Premultiplied) {
205 tmp = m_image;
206 } else if (m_image.format() == QImage::Format_RGBA16FPx4) {
207 needsConvertFP16 = true;
208 } else if (m_image.format() == QImage::Format_RGBA32FPx4) {
209 needsConvertFP32 = true;
210 } else {
211 needsConvert8 = true;
212 }
213
214 if (needsConvertFP16) {
215 tmp = m_image.convertToFormat(QImage::Format_RGBA16FPx4_Premultiplied);
216 fp16 = true;
217 } else if (needsConvertFP32) {
218 tmp = m_image.convertToFormat(QImage::Format_RGBA32FPx4_Premultiplied);
219 fp32 = true;
220 } else if (needsConvert8) {
221 tmp = m_image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
222 }
223 // Downscale the texture to fit inside the max texture limit if it is too big.
224 // It would be better if the image was already downscaled to the right size,
225 // but this information is not always available at that time, so as a last
226 // resort we can do it here. Texture coordinates are normalized, so it
227 // won't cause any problems and actual texture sizes will be written
228 // based on QSGTexture::textureSize which is updated after this, so that
229 // should be ok.
230 const int max = rhi->resourceLimit(QRhi::TextureSizeMax);
231 if (tmp.width() > max || tmp.height() > max) {
232 tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
233 m_texture_size = tmp.size();
234 }
235
236 if ((mipmapFiltering() != QSGTexture::None
237 || horizontalWrapMode() != QSGTexture::ClampToEdge
238 || verticalWrapMode() != QSGTexture::ClampToEdge)
239 && !rhi->isFeatureSupported(QRhi::NPOTTextureRepeat))
240 {
241 const int w = qNextPowerOfTwo(tmp.width() - 1);
242 const int h = qNextPowerOfTwo(tmp.height() - 1);
243 if (tmp.width() != w || tmp.height() != h) {
244 tmp = tmp.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
245 m_texture_size = tmp.size();
246 }
247 }
248
249 bool needsRebuild = false;
250
251 if (m_texture && m_texture->pixelSize() != m_texture_size) {
252 m_texture->setPixelSize(m_texture_size);
253 needsRebuild = true;
254 }
255
256 if (mipmappingChanged) {
257 QRhiTexture::Flags f = m_texture->flags();
258 f.setFlag(QRhiTexture::MipMapped, hasMipMaps);
259 f.setFlag(QRhiTexture::UsedWithGenerateMips, hasMipMaps);
260 m_texture->setFlags(f);
261 needsRebuild = true;
262 }
263
264 if (!m_texture) {
265 QRhiTexture::Flags f;
266 if (hasMipMaps)
267 f |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
268
269 QRhiTexture::Format format = QRhiTexture::RGBA8;
270 if (fp16)
271 format = QRhiTexture::RGBA16F;
272 else if (fp32)
273 format = QRhiTexture::RGBA32F;
274 else if (bgra)
275 format = QRhiTexture::BGRA8;
276
277 m_texture = rhi->newTexture(format, m_texture_size, 1, f);
278 needsRebuild = true;
279 }
280
281 if (needsRebuild) {
282 if (!m_texture->create()) {
283 qWarning("Failed to build texture for QSGPlainTexture (size %dx%d)",
284 m_texture_size.width(), m_texture_size.height());
285 return;
286 }
287 }
288
289 if (tmp.width() * 4 != tmp.bytesPerLine())
290 tmp = tmp.copy();
291
292 resourceUpdates->uploadTexture(m_texture, tmp);
293
294 if (hasMipMaps) {
295 resourceUpdates->generateMips(m_texture);
296 m_mipmaps_generated = true;
297 }
298
299 d->m_last_mipmap_filter = mipmapFiltering();
300 m_texture_rect = QRectF(0, 0, 1, 1);
301
302 if (!m_retain_image)
303 m_image = QImage();
304}
305
306QT_END_NAMESPACE
307
308#include "moc_qsgplaintexture_p.cpp"