1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt3D 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 <QtCore/qhash.h>
41 #include "texture_p.h"
42 
43 #include <private/qdebug_p.h>
44 #include <private/qrhi_p.h>
45 #include <QDebug>
46 #include <Qt3DRender/qtexture.h>
47 #include <Qt3DRender/qtexturedata.h>
48 #include <Qt3DRender/qtextureimagedata.h>
49 #include <Qt3DRender/private/managers_p.h>
50 #include <Qt3DRender/private/qabstracttexture_p.h>
51 #include <Qt3DRender/private/qtextureimagedata_p.h>
52 #include <renderbuffer_p.h>
53 #include <submissioncontext_p.h>
54 
55 QT_BEGIN_NAMESPACE
56 
57 using namespace Qt3DCore;
58 
59 namespace Qt3DRender {
60 namespace Render {
61 namespace Rhi {
62 
63 namespace {
64 
issRGBFormat(QAbstractTexture::TextureFormat format)65 bool issRGBFormat(QAbstractTexture::TextureFormat format) noexcept
66 {
67     switch (format) {
68     case QAbstractTexture::SRGB8:
69     case QAbstractTexture::SRGB8_ETC2:
70     case QAbstractTexture::SRGB8_PunchThrough_Alpha1_ETC2:
71         return true;
72     default:
73         return false;
74     }
75 }
76 
rhiFormatFromTextureFormat(QAbstractTexture::TextureFormat format)77 QRhiTexture::Format rhiFormatFromTextureFormat(QAbstractTexture::TextureFormat format) noexcept
78 {
79     switch (format) {
80     case QAbstractTexture::RGBA8_UNorm:
81     case QAbstractTexture::SRGB8:
82         return QRhiTexture::RGBA8;
83     case QAbstractTexture::R8_UNorm:
84         return QRhiTexture::R8;
85     case QAbstractTexture::R16_UNorm:
86         return QRhiTexture::R16;
87     case QAbstractTexture::RGBA16F:
88         return QRhiTexture::RGBA16F;
89     case QAbstractTexture::RGBA32F:
90         return QRhiTexture::RGBA32F;
91     case QAbstractTexture::R16F:
92         return QRhiTexture::R16F;
93     case QAbstractTexture::R32F:
94         return QRhiTexture::R32F;
95     case QAbstractTexture::D16:
96         return QRhiTexture::D16;
97     case QAbstractTexture::D32F:
98         return QRhiTexture::D32F;
99     case QAbstractTexture::RGB_DXT1:
100     case QAbstractTexture::RGBA_DXT1:
101         return QRhiTexture::BC1;
102     case QAbstractTexture::RGBA_DXT3:
103         return QRhiTexture::BC2;
104     case QAbstractTexture::RGBA_DXT5:
105         return QRhiTexture::BC3;
106     case QAbstractTexture::RGB8_ETC2:
107     case QAbstractTexture::SRGB8_ETC2:
108         return QRhiTexture::ETC2_RGB8;
109     case QAbstractTexture::RGB8_PunchThrough_Alpha1_ETC2:
110     case QAbstractTexture::SRGB8_PunchThrough_Alpha1_ETC2:
111         return QRhiTexture::ETC2_RGB8A1;
112     case QAbstractTexture::RGBA8_ETC2_EAC:
113         return QRhiTexture::ETC2_RGBA8;
114     default:
115         Q_UNREACHABLE();
116         return QRhiTexture::UnknownFormat;
117     }
118 }
119 
rhiFilterFromTextureFilter(QAbstractTexture::Filter filter)120 QRhiSampler::Filter rhiFilterFromTextureFilter(QAbstractTexture::Filter filter) noexcept
121 {
122     switch (filter) {
123     case QAbstractTexture::Nearest:
124     case QAbstractTexture::NearestMipMapNearest:
125     case QAbstractTexture::NearestMipMapLinear:
126         return QRhiSampler::Nearest;
127     case QAbstractTexture::Linear:
128     case QAbstractTexture::LinearMipMapNearest:
129     case QAbstractTexture::LinearMipMapLinear:
130         return QRhiSampler::Linear;
131     default:
132         Q_UNREACHABLE();
133         return QRhiSampler::Nearest;
134     }
135 }
136 
rhiMipMapFilterFromTextureFilter(QAbstractTexture::Filter filter)137 QRhiSampler::Filter rhiMipMapFilterFromTextureFilter(QAbstractTexture::Filter filter) noexcept
138 {
139     switch (filter) {
140     case QAbstractTexture::NearestMipMapNearest:
141     case QAbstractTexture::LinearMipMapNearest:
142         return QRhiSampler::Nearest;
143     case QAbstractTexture::Linear:
144     case QAbstractTexture::NearestMipMapLinear:
145     case QAbstractTexture::LinearMipMapLinear:
146         return QRhiSampler::Linear;
147     default:
148         Q_UNREACHABLE();
149         return QRhiSampler::None;
150     }
151 }
152 
153 std::tuple<QRhiSampler::AddressMode, QRhiSampler::AddressMode, QRhiSampler::AddressMode>
rhiWrapModeFromTextureWrapMode(QTextureWrapMode::WrapMode x,QTextureWrapMode::WrapMode y,QTextureWrapMode::WrapMode z)154 rhiWrapModeFromTextureWrapMode(QTextureWrapMode::WrapMode x, QTextureWrapMode::WrapMode y,
155                                QTextureWrapMode::WrapMode z) noexcept
156 {
157     auto toRhiAddress = [](QTextureWrapMode::WrapMode mode) noexcept {
158         switch (mode) {
159         case Qt3DRender::QTextureWrapMode::Repeat:
160             return QRhiSampler::Repeat;
161         case Qt3DRender::QTextureWrapMode::ClampToEdge:
162         case Qt3DRender::QTextureWrapMode::ClampToBorder:
163             return QRhiSampler::ClampToEdge;
164         case Qt3DRender::QTextureWrapMode::MirroredRepeat:
165             return QRhiSampler::Mirror;
166         default:
167             Q_UNREACHABLE();
168         }
169     };
170 
171     return { toRhiAddress(x), toRhiAddress(y), toRhiAddress(z) };
172 }
173 
174 QRhiSampler::CompareOp
rhiCompareOpFromTextureCompareOp(QAbstractTexture::ComparisonFunction mode)175 rhiCompareOpFromTextureCompareOp(QAbstractTexture::ComparisonFunction mode) noexcept
176 {
177     switch (mode) {
178     case QAbstractTexture::CompareLessEqual:
179         return QRhiSampler::LessOrEqual;
180     case QAbstractTexture::CompareGreaterEqual:
181         return QRhiSampler::GreaterOrEqual;
182     case QAbstractTexture::CompareLess:
183         return QRhiSampler::Less;
184     case QAbstractTexture::CompareGreater:
185         return QRhiSampler::Greater;
186     case QAbstractTexture::CompareEqual:
187         return QRhiSampler::Equal;
188     case QAbstractTexture::CommpareNotEqual:
189         return QRhiSampler::NotEqual;
190     case QAbstractTexture::CompareAlways:
191         return QRhiSampler::Always;
192     case QAbstractTexture::CompareNever:
193         return QRhiSampler::Never;
194     default:
195         return QRhiSampler::Always;
196     }
197 }
198 
199 // This uploadGLData where the data is a fullsize subimage
200 // as QOpenGLTexture doesn't allow partial subimage uploads
createUploadEntry(int level,int layer,const QByteArray & bytes)201 QRhiTextureUploadEntry createUploadEntry(int level, int layer, const QByteArray &bytes) noexcept
202 {
203     QRhiTextureSubresourceUploadDescription description;
204     description.setData(bytes);
205     return QRhiTextureUploadEntry(layer, level, description);
206 }
207 
208 template<typename F>
filterLayersAndFaces(const QTextureImageData & data,F f)209 void filterLayersAndFaces(const QTextureImageData &data, F f)
210 {
211     const int layers = data.layers();
212     const int faces = data.faces();
213     const int miplevels = data.mipLevels();
214 
215     if (layers == 1 && faces == 1) {
216         for (int level = 0; level < miplevels; level++) {
217             f(createUploadEntry(level, 0, data.data(0, 0, level)));
218         }
219     } else if (layers > 1 && faces == 1) {
220         qWarning() << Q_FUNC_INFO << "Unsupported case, see QTBUG-83343";
221         /*
222         for (int layer = 0; layer < data.layers(); layer++) {
223             for (int level = 0; level < mipLevels; level++) {
224                 f(createUploadEntry(level, layer, data.data(layer, 0, level)));
225             }
226         }
227         */
228     } else if (faces > 1 && layers == 1) {
229         // Mip levels do not seem to be supported by cubemaps...
230         for (int face = 0; face < data.faces(); face++) {
231             f(createUploadEntry(0, face, data.data(0, face, 0)));
232         }
233     } else {
234         qWarning() << Q_FUNC_INFO << "Unsupported case";
235     }
236 }
237 
238 template<typename F>
filterLayerAndFace(int layer,int face,F f)239 void filterLayerAndFace(int layer, int face, F f)
240 {
241     if (layer == 0 && face == 0) {
242         f(0);
243     } else if (layer > 0 && face == 0) {
244         qWarning() << Q_FUNC_INFO << "Unsupported case, see QTBUG-83343";
245         // f(layer);
246     } else if (layer == 0 && face > 0) {
247         f(face);
248     } else {
249         qWarning() << Q_FUNC_INFO << "Unsupported case";
250     }
251 }
252 
253 // For partial sub image uploads
createUploadEntry(int mipLevel,int layer,int xOffset,int yOffset,int zOffset,const QByteArray & bytes,const QTextureImageDataPtr & data)254 QRhiTextureUploadEntry createUploadEntry(int mipLevel, int layer, int xOffset, int yOffset,
255                                          int zOffset, const QByteArray &bytes,
256                                          const QTextureImageDataPtr &data) noexcept
257 {
258     QRhiTextureSubresourceUploadDescription description;
259     description.setData(bytes);
260     description.setSourceTopLeft(QPoint(xOffset, yOffset));
261     return QRhiTextureUploadEntry(layer, mipLevel, description);
262 }
263 
264 } // anonymous
265 
RHITexture()266 RHITexture::RHITexture()
267     : m_dirtyFlags(None),
268       m_rhi(nullptr),
269       m_rhiSampler(nullptr),
270       m_renderBuffer(nullptr),
271       m_dataFunctor(),
272       m_pendingDataFunctor(nullptr),
273       m_sharedTextureId(-1),
274       m_externalRendering(false),
275       m_wasTextureRecreated(false)
276 {
277 }
278 
~RHITexture()279 RHITexture::~RHITexture() { }
280 
281 // Must be called from RenderThread with active GL context
destroy()282 void RHITexture::destroy()
283 {
284     delete m_rhi;
285     m_rhi = nullptr;
286     delete m_rhiSampler;
287     m_rhiSampler = nullptr;
288     delete m_renderBuffer;
289     m_renderBuffer = nullptr;
290 
291     m_dirtyFlags = None;
292     m_sharedTextureId = -1;
293     m_externalRendering = false;
294     m_wasTextureRecreated = false;
295     m_dataFunctor.reset();
296     m_pendingDataFunctor = nullptr;
297 
298     m_properties = {};
299     m_parameters = {};
300     m_textureData.reset();
301     m_images.clear();
302     m_imageData.clear();
303     m_pendingTextureDataUpdates.clear();
304 }
305 
loadTextureDataFromGenerator()306 bool RHITexture::loadTextureDataFromGenerator()
307 {
308     m_textureData = m_dataFunctor->operator()();
309     // if there is a texture generator, most properties will be defined by it
310     if (m_textureData) {
311         const QAbstractTexture::Target target = m_textureData->target();
312 
313         // If both target and functor return Automatic we are still
314         // probably loading the texture, return false
315         if (m_properties.target == QAbstractTexture::TargetAutomatic
316             && target == QAbstractTexture::TargetAutomatic) {
317             m_textureData.reset();
318             return false;
319         }
320 
321         if (m_properties.target != QAbstractTexture::TargetAutomatic
322             && target != QAbstractTexture::TargetAutomatic && m_properties.target != target) {
323             qWarning() << Q_FUNC_INFO
324                        << "Generator and Properties not requesting the same texture target";
325             m_textureData.reset();
326             return false;
327         }
328 
329         // We take target type from generator if it wasn't explicitly set by the user
330         if (m_properties.target == QAbstractTexture::TargetAutomatic)
331             m_properties.target = target;
332         m_properties.width = m_textureData->width();
333         m_properties.height = m_textureData->height();
334         m_properties.depth = m_textureData->depth();
335         m_properties.layers = m_textureData->layers();
336         m_properties.format = m_textureData->format();
337 
338         const QVector<QTextureImageDataPtr> imageData = m_textureData->imageData();
339 
340         if (!imageData.empty()) {
341             // Set the mips level based on the first image if autoMipMapGeneration is disabled
342             if (!m_properties.generateMipMaps)
343                 m_properties.mipLevels = imageData.first()->mipLevels();
344         }
345     }
346     return !m_textureData.isNull();
347 }
348 
loadTextureDataFromImages()349 void RHITexture::loadTextureDataFromImages()
350 {
351     int maxMipLevel = 0;
352     for (const Image &img : qAsConst(m_images)) {
353         const QTextureImageDataPtr imgData = img.generator->operator()();
354         // imgData may be null in the following cases:
355         // - Texture is created with TextureImages which have yet to be
356         // loaded (skybox where you don't yet know the path, source set by
357         // a property binding, queued connection ...)
358         // - TextureImage whose generator failed to return a valid data
359         // (invalid url, error opening file...)
360         if (imgData.isNull())
361             continue;
362 
363         m_imageData.push_back(imgData);
364         maxMipLevel = qMax(maxMipLevel, img.mipLevel);
365 
366         // If the texture doesn't have a texture generator, we will
367         // derive some properties from the first TextureImage (layer=0, miplvl=0, face=0)
368         if (!m_textureData && img.layer == 0 && img.mipLevel == 0
369             && img.face == QAbstractTexture::CubeMapPositiveX) {
370             if (imgData->width() != -1 && imgData->height() != -1 && imgData->depth() != -1) {
371                 m_properties.width = imgData->width();
372                 m_properties.height = imgData->height();
373                 m_properties.depth = imgData->depth();
374             }
375             // Set the format of the texture if the texture format is set to Automatic
376             if (m_properties.format == QAbstractTexture::Automatic) {
377                 m_properties.format =
378                         static_cast<QAbstractTexture::TextureFormat>(imgData->format());
379             }
380             setDirtyFlag(Properties, true);
381         }
382     }
383 
384     // make sure the number of mip levels is set when there is no texture data generator
385     if (!m_dataFunctor) {
386         m_properties.mipLevels = maxMipLevel + 1;
387         setDirtyFlag(Properties, true);
388     }
389 }
390 
391 // Called from RenderThread
createOrUpdateRhiTexture(SubmissionContext * ctx)392 RHITexture::TextureUpdateInfo RHITexture::createOrUpdateRhiTexture(SubmissionContext *ctx)
393 {
394     TextureUpdateInfo textureInfo;
395     m_wasTextureRecreated = false;
396 
397     const bool hasSharedTextureId = m_sharedTextureId > 0;
398     // Only load texture data if we are not using a sharedTextureId
399     // Check if dataFunctor or images have changed
400     if (!hasSharedTextureId) {
401         // If dataFunctor exists and we have no data and it hasn´t run yet
402         if (m_dataFunctor && !m_textureData && m_dataFunctor.get() != m_pendingDataFunctor) {
403             const bool successfullyLoadedTextureData = loadTextureDataFromGenerator();
404             // If successful, m_textureData has content
405             if (successfullyLoadedTextureData) {
406                 setDirtyFlag(Properties, true);
407                 setDirtyFlag(TextureData, true);
408             } else {
409                 if (m_pendingDataFunctor != m_dataFunctor.get()) {
410                     qWarning() << "[Qt3DRender::RHITexture] No QTextureData generated from Texture "
411                                   "Generator yet. Texture will be invalid for this frame";
412                     m_pendingDataFunctor = m_dataFunctor.get();
413                 }
414                 textureInfo.properties.status = QAbstractTexture::Loading;
415                 return textureInfo;
416             }
417         }
418 
419         // If images have changed, clear previous images data
420         // and regenerate m_imageData for the images
421         if (testDirtyFlag(TextureImageData)) {
422             m_imageData.clear();
423             loadTextureDataFromImages();
424             // Mark for upload if we actually have something to upload
425             if (!m_imageData.empty()) {
426                 setDirtyFlag(TextureData, true);
427             }
428             // Reset image flag
429             setDirtyFlag(TextureImageData, false);
430         }
431 
432         // Don't try to create the texture if the target or format was still not set
433         // Format should either be set by user or if Automatic
434         // by either the dataGenerator of the texture or the first Image
435         // Target should explicitly be set by the user or the dataGenerator
436         if (m_properties.target == QAbstractTexture::TargetAutomatic
437             || m_properties.format == QAbstractTexture::Automatic
438             || m_properties.format == QAbstractTexture::NoFormat) {
439             textureInfo.properties.status = QAbstractTexture::Error;
440             return textureInfo;
441         }
442     }
443 
444     // If the properties changed or texture has become a shared texture from a
445     // 3rd party engine, we need to destroy and maybe re-allocate the texture
446     if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) {
447         delete m_rhi;
448         m_rhi = nullptr;
449         textureInfo.wasUpdated = true;
450         // If we are destroyed because of some property change but still have (some) of
451         // our content data make sure we are marked for upload
452         // TO DO: We should actually check if the textureData is still correct
453         // in regard to the size, target and format of the texture though.
454         if (!testDirtyFlag(SharedTextureId)
455             && (m_textureData || !m_imageData.empty() || !m_pendingTextureDataUpdates.empty()))
456             setDirtyFlag(TextureData, true);
457     }
458 
459     m_properties.status = QAbstractTexture::Ready;
460 
461     if (testDirtyFlag(SharedTextureId) || hasSharedTextureId) {
462         // Update m_properties by doing introspection on the texture
463         if (hasSharedTextureId)
464             introspectPropertiesFromSharedTextureId();
465         setDirtyFlag(SharedTextureId, false);
466     } else {
467         // We only build a QOpenGLTexture if we have no shared textureId set
468         if (!m_rhi) {
469             m_rhi = buildRhiTexture(ctx);
470             if (!m_rhi) {
471                 qWarning() << "[Qt3DRender::RHITexture] failed to create texture";
472                 textureInfo.properties.status = QAbstractTexture::Error;
473                 return textureInfo;
474             }
475             m_wasTextureRecreated = true;
476         }
477 
478         textureInfo.texture = m_rhi;
479 
480         // need to (re-)upload texture data?
481         const bool needsUpload = testDirtyFlag(TextureData);
482         if (needsUpload) {
483             uploadRhiTextureData(ctx);
484             setDirtyFlag(TextureData, false);
485         }
486 
487         // need to set texture parameters?
488         if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
489             updateRhiTextureParameters(ctx);
490             setDirtyFlag(Properties, false);
491             setDirtyFlag(Parameters, false);
492         }
493     }
494 
495     textureInfo.properties = m_properties;
496 
497     return textureInfo;
498 }
499 
getOrCreateRenderBuffer()500 RenderBuffer *RHITexture::getOrCreateRenderBuffer()
501 {
502     if (m_dataFunctor && !m_textureData) {
503         m_textureData = m_dataFunctor->operator()();
504         if (m_textureData) {
505             if (m_properties.target != QAbstractTexture::TargetAutomatic)
506                 qWarning() << "[Qt3DRender::RHITexture] [renderbuffer] When a texture provides a "
507                               "generator, it's target is expected to be TargetAutomatic";
508 
509             m_properties.width = m_textureData->width();
510             m_properties.height = m_textureData->height();
511             m_properties.format = m_textureData->format();
512 
513             setDirtyFlag(Properties);
514         } else {
515             if (m_pendingDataFunctor != m_dataFunctor.get()) {
516                 qWarning() << "[Qt3DRender::RHITexture] [renderbuffer] No QTextureData generated "
517                               "from Texture Generator yet. Texture will be invalid for this frame";
518                 m_pendingDataFunctor = m_dataFunctor.get();
519             }
520             return nullptr;
521         }
522     }
523 
524     if (testDirtyFlag(Properties)) {
525         delete m_renderBuffer;
526         m_renderBuffer = nullptr;
527     }
528 
529     if (!m_renderBuffer)
530         m_renderBuffer =
531                 new RenderBuffer(m_properties.width, m_properties.height, m_properties.format);
532 
533     setDirtyFlag(Properties, false);
534     setDirtyFlag(Parameters, false);
535 
536     return m_renderBuffer;
537 }
538 
539 // This must be called from the RenderThread
540 // So RHITexture release from the manager can only be done from that thread
cleanup()541 void RHITexture::cleanup()
542 {
543     destroy();
544 }
545 
setParameters(const TextureParameters & params)546 void RHITexture::setParameters(const TextureParameters &params)
547 {
548     if (m_parameters != params) {
549         m_parameters = params;
550         setDirtyFlag(Parameters);
551     }
552 }
553 
setProperties(const TextureProperties & props)554 void RHITexture::setProperties(const TextureProperties &props)
555 {
556     if (m_properties != props) {
557         m_properties = props;
558         setDirtyFlag(Properties);
559     }
560 }
561 
setImages(const QVector<Image> & images)562 void RHITexture::setImages(const QVector<Image> &images)
563 {
564     // check if something has changed at all
565     bool same = (images.size() == m_images.size());
566     if (same) {
567         for (int i = 0; i < images.size(); i++) {
568             if (images[i] != m_images[i]) {
569                 same = false;
570                 break;
571             }
572         }
573     }
574 
575     if (!same) {
576         m_images = images;
577         requestImageUpload();
578     }
579 }
580 
setGenerator(const QTextureGeneratorPtr & generator)581 void RHITexture::setGenerator(const QTextureGeneratorPtr &generator)
582 {
583     m_textureData.reset();
584     m_dataFunctor = generator;
585     m_pendingDataFunctor = nullptr;
586     requestUpload();
587 }
588 
setSharedTextureId(int textureId)589 void RHITexture::setSharedTextureId(int textureId)
590 {
591     if (m_sharedTextureId != textureId) {
592         m_sharedTextureId = textureId;
593         setDirtyFlag(SharedTextureId);
594     }
595 }
596 
addTextureDataUpdates(const QVector<QTextureDataUpdate> & updates)597 void RHITexture::addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates)
598 {
599     m_pendingTextureDataUpdates += updates;
600     requestUpload();
601 }
602 
603 // Return nullptr if
604 // - context cannot be obtained
605 // - texture hasn't yet been loaded
buildRhiTexture(SubmissionContext * ctx)606 QRhiTexture *RHITexture::buildRhiTexture(SubmissionContext *ctx)
607 {
608     const QAbstractTexture::Target actualTarget = m_properties.target;
609     if (actualTarget == QAbstractTexture::TargetAutomatic) {
610         // If the target is automatic at this point, it means that the texture
611         // hasn't been loaded yet (case of remote urls) and that loading failed
612         // and that target format couldn't be deduced
613         return nullptr;
614     }
615 
616     const QRhiTexture::Format rhiFormat = rhiFormatFromTextureFormat(m_properties.format);
617     const QSize pixelSize(m_properties.width, m_properties.height);
618     QRhiTexture::Flags rhiFlags {};
619     int sampleCount = 1;
620 
621     const bool issRGB8Format = issRGBFormat(m_properties.format);
622     if (issRGB8Format)
623         rhiFlags |= QRhiTexture::sRGB;
624 
625     if (actualTarget == QAbstractTexture::Target2DMultisample
626         || actualTarget == QAbstractTexture::Target2DMultisampleArray) {
627         // Set samples count if multisampled texture
628         // (multisampled textures don't have mipmaps)
629         sampleCount = m_properties.samples;
630     }
631 
632     switch (actualTarget) {
633     case QAbstractTexture::TargetCubeMap:
634     case QAbstractTexture::TargetCubeMapArray: {
635         rhiFlags |= QRhiTexture::CubeMap;
636         break;
637     }
638     default: {
639         // Mipmaps don't see to work with cubemaps at the moment
640         if (m_properties.generateMipMaps) {
641             rhiFlags |= QRhiTexture::UsedWithGenerateMips;
642             rhiFlags |= QRhiTexture::MipMapped;
643         } else if (m_properties.mipLevels > 1) {
644             rhiFlags |= QRhiTexture::MipMapped;
645         }
646         break;
647     }
648     }
649 
650     QRhiTexture *rhiTexture = ctx->rhi()->newTexture(rhiFormat, pixelSize, sampleCount, rhiFlags);
651 
652     if (!rhiTexture->build()) {
653         qWarning() << Q_FUNC_INFO << "creating QRhiTexture failed";
654         delete rhiTexture;
655         return nullptr;
656     }
657     return rhiTexture;
658 }
659 
uploadRhiTextureData(SubmissionContext * ctx)660 void RHITexture::uploadRhiTextureData(SubmissionContext *ctx)
661 {
662     QVarLengthArray<QRhiTextureUploadEntry> uploadEntries;
663 
664     // Upload all QTexImageData set by the QTextureGenerator
665     if (m_textureData) {
666         const QVector<QTextureImageDataPtr> &imgData = m_textureData->imageData();
667 
668         for (const QTextureImageDataPtr &data : imgData) {
669             const int mipLevels = data->mipLevels();
670             Q_ASSERT(mipLevels <= ctx->rhi()->mipLevelsForSize({ data->width(), data->height() }));
671 
672             filterLayersAndFaces(*data, [&](QRhiTextureUploadEntry &&entry) {
673                 uploadEntries.push_back(std::move(entry));
674             });
675         }
676     }
677 
678     // Upload all QTexImageData references by the TextureImages
679     for (int i = 0; i < std::min(m_images.size(), m_imageData.size()); i++) {
680         const QTextureImageDataPtr &imgData = m_imageData.at(i);
681         // Here the bytes in the QTextureImageData contain data for a single
682         // layer, face or mip level, unlike the QTextureGenerator case where
683         // they are in a single blob. Hence QTextureImageData::data() is not suitable.
684         const QByteArray bytes(QTextureImageDataPrivate::get(imgData.get())->m_data);
685         const int layer = m_images[i].layer;
686         const int face = m_images[i].face;
687         filterLayerAndFace(layer, face, [&](int rhiLayer) {
688             uploadEntries.push_back(createUploadEntry(m_images[i].mipLevel, rhiLayer, bytes));
689         });
690     }
691 
692     // Free up image data once content has been uploaded
693     // Note: if data functor stores the data, this won't really free anything though
694     m_imageData.clear();
695 
696     // Update data from TextureUpdates
697     const QVector<QTextureDataUpdate> textureDataUpdates = std::move(m_pendingTextureDataUpdates);
698     for (const QTextureDataUpdate &update : textureDataUpdates) {
699         const QTextureImageDataPtr imgData = update.data();
700 
701         if (!imgData) {
702             qWarning() << Q_FUNC_INFO << "QTextureDataUpdate no QTextureImageData set";
703             continue;
704         }
705 
706         // TO DO: There's currently no way to check the depth of an existing QRhiTexture
707         const int xOffset = update.x();
708         const int yOffset = update.y();
709         const int xExtent = xOffset + imgData->width();
710         const int yExtent = yOffset + imgData->height();
711 
712         // Check update is compatible with our texture
713         if (xOffset >= m_rhi->pixelSize().width() || yOffset >= m_rhi->pixelSize().height()
714             || xExtent > m_rhi->pixelSize().width() || yExtent > m_rhi->pixelSize().height()) {
715             qWarning() << Q_FUNC_INFO << "QTextureDataUpdate incompatible with texture";
716             continue;
717         }
718 
719         const QByteArray bytes = (QTextureImageDataPrivate::get(imgData.get())->m_data);
720         // Here the bytes in the QTextureImageData contain data for a single
721         // layer, face or mip level, unlike the QTextureGenerator case where
722         // they are in a single blob. Hence QTextureImageData::data() is not suitable.
723 
724         const int layer = update.layer();
725         const int face = update.face();
726         filterLayerAndFace(layer, face, [&](int rhiLayer) {
727             const QRhiTextureUploadEntry entry = createUploadEntry(
728                     update.mipLevel(), rhiLayer, xOffset, yOffset, 0, bytes, imgData);
729             uploadEntries.push_back(entry);
730         });
731     }
732 
733     QRhiTextureUploadDescription uploadDescription;
734     uploadDescription.setEntries(uploadEntries.begin(), uploadEntries.end());
735 
736     ctx->m_currentUpdates->uploadTexture(m_rhi, uploadDescription);
737     if (m_properties.generateMipMaps)
738         ctx->m_currentUpdates->generateMips(m_rhi);
739 }
740 
updateRhiTextureParameters(SubmissionContext * ctx)741 void RHITexture::updateRhiTextureParameters(SubmissionContext *ctx)
742 {
743     const QAbstractTexture::Target actualTarget = m_properties.target;
744     const bool isMultisampledTexture =
745             (actualTarget == QAbstractTexture::Target2DMultisample
746              || actualTarget == QAbstractTexture::Target2DMultisampleArray);
747     // Multisampled textures can only be accessed by texelFetch in shaders
748     // and don't support wrap modes and mig/mag filtes
749     if (isMultisampledTexture)
750         return;
751 
752     // TO DO:
753     if (m_rhiSampler) {
754         delete m_rhiSampler;
755         m_rhiSampler = nullptr;
756     }
757 
758     const QRhiSampler::Filter magFilter =
759             rhiFilterFromTextureFilter(m_parameters.magnificationFilter);
760     const QRhiSampler::Filter minFilter =
761             rhiFilterFromTextureFilter(m_parameters.minificationFilter);
762     const QRhiSampler::Filter mipMapFilter =
763             rhiMipMapFilterFromTextureFilter(m_parameters.magnificationFilter);
764     const auto wrapMode = rhiWrapModeFromTextureWrapMode(
765             m_parameters.wrapModeX, m_parameters.wrapModeY, m_parameters.wrapModeZ);
766     const QRhiSampler::CompareOp compareOp =
767             rhiCompareOpFromTextureCompareOp(m_parameters.comparisonFunction);
768     m_rhiSampler = ctx->rhi()->newSampler(magFilter, minFilter, mipMapFilter, std::get<0>(wrapMode),
769                                           std::get<1>(wrapMode), std::get<2>(wrapMode));
770 
771     m_rhiSampler->setTextureCompareOp(compareOp);
772 
773     if (!m_rhiSampler->build()) {
774         qDebug("Could not build RHI texture sampler");
775     }
776 }
777 
introspectPropertiesFromSharedTextureId()778 void RHITexture::introspectPropertiesFromSharedTextureId()
779 {
780     //    // We know that the context is active when this function is called
781     //    QOpenGLContext *ctx = QOpenGLContext::currentContext();
782     //    if (!ctx) {
783     //        qWarning() << Q_FUNC_INFO << "requires an OpenGL context";
784     //        return;
785     //    }
786     //    QOpenGLFunctions *gl = ctx->functions();
787 
788     //    // If the user has set the target format himself, we won't try to deduce it
789     //    if (m_properties.target != QAbstractTexture::TargetAutomatic)
790     //        return;
791 
792     //    const QAbstractTexture::Target targets[] = {
793     //        QAbstractTexture::Target2D,
794     //        QAbstractTexture::TargetCubeMap,
795     //#ifndef QT_OPENGL_ES_2
796     //        QAbstractTexture::Target1D,
797     //        QAbstractTexture::Target1DArray,
798     //        QAbstractTexture::Target3D,
799     //        QAbstractTexture::Target2DArray,
800     //        QAbstractTexture::TargetCubeMapArray,
801     //        QAbstractTexture::Target2DMultisample,
802     //        QAbstractTexture::Target2DMultisampleArray,
803     //        QAbstractTexture::TargetRectangle,
804     //        QAbstractTexture::TargetBuffer,
805     //#endif
806     //    };
807 
808     //#ifndef QT_OPENGL_ES_2
809     //    // Try to find texture target with GL 4.5 functions
810     //    const QPair<int, int> ctxGLVersion = ctx->format().version();
811     //    if (ctxGLVersion.first > 4 || (ctxGLVersion.first == 4 && ctxGLVersion.second >= 5)) {
812     //        // Only for GL 4.5+
813     //#ifdef GL_TEXTURE_TARGET
814     //        QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>();
815     //        if (gl5 != nullptr)
816     //            gl5->glGetTextureParameteriv(m_sharedTextureId, GL_TEXTURE_TARGET,
817     //            reinterpret_cast<int *>(&m_properties.target));
818     //#endif
819     //    }
820     //#endif
821 
822     //    // If GL 4.5 function unavailable or not working, try a slower way
823     //    if (m_properties.target == QAbstractTexture::TargetAutomatic) {
824     //        //    // OpenGL offers no proper way of querying for the target of a texture given its
825     //        id gl->glActiveTexture(GL_TEXTURE0);
826 
827     //        const GLenum targetBindings[] = {
828     //            GL_TEXTURE_BINDING_2D,
829     //            GL_TEXTURE_BINDING_CUBE_MAP,
830     //#ifndef QT_OPENGL_ES_2
831     //            GL_TEXTURE_BINDING_1D,
832     //            GL_TEXTURE_BINDING_1D_ARRAY,
833     //            GL_TEXTURE_BINDING_3D,
834     //            GL_TEXTURE_BINDING_2D_ARRAY,
835     //            GL_TEXTURE_BINDING_CUBE_MAP_ARRAY,
836     //            GL_TEXTURE_BINDING_2D_MULTISAMPLE,
837     //            GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,
838     //            GL_TEXTURE_BINDING_RECTANGLE,
839     //            GL_TEXTURE_BINDING_BUFFER
840     //#endif
841     //        };
842 
843     //        Q_ASSERT(sizeof(targetBindings) / sizeof(targetBindings[0] == sizeof(targets) /
844     //        sizeof(targets[0])));
845 
846     //        for (uint i = 0; i < sizeof(targetBindings) / sizeof(targetBindings[0]); ++i) {
847     //            const int target = targets[i];
848     //            gl->glBindTexture(target, m_sharedTextureId);
849     //            int boundId = 0;
850     //            gl->glGetIntegerv(targetBindings[i], &boundId);
851     //            gl->glBindTexture(target, 0);
852     //            if (boundId == m_sharedTextureId) {
853     //                m_properties.target = static_cast<QAbstractTexture::Target>(target);
854     //                break;
855     //            }
856     //        }
857     //    }
858 
859     //    // Return early if we weren't able to find texture target
860     //    if (std::find(std::begin(targets), std::end(targets), m_properties.target) ==
861     //    std::end(targets)) {
862     //        qWarning() << "Unable to determine texture target for shared GL texture";
863     //        return;
864     //    }
865 
866     //    // Bind texture once we know its target
867     //    gl->glBindTexture(m_properties.target, m_sharedTextureId);
868 
869     //    // TO DO: Improve by using glGetTextureParameters when available which
870     //    // support direct state access
871     //#ifndef GL_TEXTURE_MAX_LEVEL
872     //#define GL_TEXTURE_MAX_LEVEL        0x813D
873     //#endif
874 
875     //#ifndef GL_TEXTURE_WRAP_R
876     //#define GL_TEXTURE_WRAP_R           0x8072
877     //#endif
878 
879     //    gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAX_LEVEL,
880     //    reinterpret_cast<int *>(&m_properties.mipLevels));
881     //    gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MIN_FILTER,
882     //    reinterpret_cast<int *>(&m_parameters.minificationFilter));
883     //    gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAG_FILTER,
884     //    reinterpret_cast<int *>(&m_parameters.magnificationFilter));
885     //    gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_R, reinterpret_cast<int
886     //    *>(&m_parameters.wrapModeX)); gl->glGetTexParameteriv(int(m_properties.target),
887     //    GL_TEXTURE_WRAP_S, reinterpret_cast<int *>(&m_parameters.wrapModeY));
888     //    gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_T, reinterpret_cast<int
889     //    *>(&m_parameters.wrapModeZ));
890 
891     //#ifndef QT_OPENGL_ES_2
892     //    // Try to retrieve dimensions (not available on ES 2.0)
893     //    if (!ctx->isOpenGLES()) {
894     //        QOpenGLFunctions_3_1 *gl3 = ctx->versionFunctions<QOpenGLFunctions_3_1>();
895     //        if (!gl3) {
896     //            qWarning() << "Failed to retrieve shared texture dimensions";
897     //            return;
898     //        }
899 
900     //        gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_WIDTH,
901     //        reinterpret_cast<int *>(&m_properties.width));
902     //        gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_HEIGHT,
903     //        reinterpret_cast<int *>(&m_properties.height));
904     //        gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_DEPTH,
905     //        reinterpret_cast<int *>(&m_properties.depth));
906     //        gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_INTERNAL_FORMAT,
907     //        reinterpret_cast<int *>(&m_properties.format));
908     //    }
909     //#endif
910 
911     //    gl->glBindTexture(m_properties.target, 0);
912 }
913 
914 } // namespace Rhi
915 } // namespace Render
916 } // namespace Qt3DRender
917 
918 QT_END_NAMESPACE
919