1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "gltexture_p.h"
42 
43 #include <private/qdebug_p.h>
44 #include <private/qopengltexture_p.h>
45 #include <private/qopengltexturehelper_p.h>
46 #include <QDebug>
47 #include <QOpenGLFunctions>
48 #include <QOpenGLTexture>
49 #include <QOpenGLPixelTransferOptions>
50 #include <Qt3DRender/qtexture.h>
51 #include <Qt3DRender/qtexturedata.h>
52 #include <Qt3DRender/qtextureimagedata.h>
53 #include <Qt3DRender/private/managers_p.h>
54 #include <Qt3DRender/private/qabstracttexture_p.h>
55 #include <Qt3DRender/private/qtextureimagedata_p.h>
56 #include <renderbuffer_p.h>
57 
58 #if !defined(QT_OPENGL_ES_2)
59 #include <QOpenGLFunctions_3_1>
60 #include <QOpenGLFunctions_4_5_Core>
61 #endif
62 
63 QT_BEGIN_NAMESPACE
64 
65 using namespace Qt3DCore;
66 
67 namespace Qt3DRender {
68 namespace Render {
69 namespace OpenGL {
70 
71 namespace {
72 
73 // This uploadGLData where the data is a fullsize subimage
74 // as QOpenGLTexture doesn't allow partial subimage uploads
uploadGLData(QOpenGLTexture * glTex,int level,int layer,QOpenGLTexture::CubeMapFace face,const QByteArray & bytes,const QTextureImageDataPtr & data)75 void uploadGLData(QOpenGLTexture *glTex,
76                   int level, int layer, QOpenGLTexture::CubeMapFace face,
77                   const QByteArray &bytes, const QTextureImageDataPtr &data)
78 {
79     const auto alignment = QTextureImageDataPrivate::get(data.get())->m_alignment;
80     QOpenGLPixelTransferOptions uploadOptions;
81     uploadOptions.setAlignment(alignment);
82     if (data->isCompressed())
83         glTex->setCompressedData(level, layer, face, bytes.size(), bytes.constData(), &uploadOptions);
84     else
85         glTex->setData(level, layer, face, data->pixelFormat(), data->pixelType(), bytes.constData(), &uploadOptions);
86 }
87 
88 // For partial sub image uploads
uploadGLData(QOpenGLTexture * glTex,int mipLevel,int layer,QOpenGLTexture::CubeMapFace cubeFace,int xOffset,int yOffset,int zOffset,const QByteArray & bytes,const QTextureImageDataPtr & data)89 void uploadGLData(QOpenGLTexture *glTex,
90                   int mipLevel, int layer, QOpenGLTexture::CubeMapFace cubeFace,
91                   int xOffset, int yOffset, int zOffset,
92                   const QByteArray &bytes, const QTextureImageDataPtr &data)
93 {
94     if (data->isCompressed()) {
95         Q_UNREACHABLE();
96     } else {
97         const auto alignment = QTextureImageDataPrivate::get(data.get())->m_alignment;
98         QOpenGLPixelTransferOptions uploadOptions;
99         uploadOptions.setAlignment(alignment);
100         glTex->setData(xOffset, yOffset, zOffset,
101                        data->width(), data->height(), data->depth(),
102                        mipLevel, layer, cubeFace, data->layers(),
103                        data->pixelFormat(), data->pixelType(),
104                        bytes.constData(), &uploadOptions);
105     }
106 }
107 
108 } // anonymous
109 
110 
GLTexture()111 GLTexture::GLTexture()
112     : m_dirtyFlags(None)
113     , m_gl(nullptr)
114     , m_renderBuffer(nullptr)
115     , m_dataFunctor()
116     , m_pendingDataFunctor(nullptr)
117     , m_sharedTextureId(-1)
118     , m_externalRendering(false)
119     , m_wasTextureRecreated(false)
120 {
121 }
122 
~GLTexture()123 GLTexture::~GLTexture()
124 {
125 }
126 
127 // Must be called from RenderThread with active GL context
destroy()128 void GLTexture::destroy()
129 {
130     delete m_gl;
131     m_gl = nullptr;
132     delete m_renderBuffer;
133     m_renderBuffer = nullptr;
134 
135     m_dirtyFlags = None;
136     m_sharedTextureId = -1;
137     m_externalRendering = false;
138     m_wasTextureRecreated = false;
139     m_dataFunctor.reset();
140     m_pendingDataFunctor = nullptr;
141 
142     m_properties = {};
143     m_parameters = {};
144     m_textureData.reset();
145     m_images.clear();
146     m_imageData.clear();
147     m_pendingTextureDataUpdates.clear();
148 }
149 
loadTextureDataFromGenerator()150 bool GLTexture::loadTextureDataFromGenerator()
151 {
152     m_textureData = m_dataFunctor->operator()();
153     // if there is a texture generator, most properties will be defined by it
154     if (m_textureData) {
155         const QAbstractTexture::Target target = m_textureData->target();
156 
157         // If both target and functor return Automatic we are still
158         // probably loading the texture, return false
159         if (m_properties.target == QAbstractTexture::TargetAutomatic &&
160             target == QAbstractTexture::TargetAutomatic) {
161             m_textureData.reset();
162             return false;
163         }
164 
165         if (m_properties.target != QAbstractTexture::TargetAutomatic &&
166             target != QAbstractTexture::TargetAutomatic &&
167             m_properties.target != target) {
168             qWarning() << Q_FUNC_INFO << "Generator and Properties not requesting the same texture target";
169             m_textureData.reset();
170             return false;
171         }
172 
173         // We take target type from generator if it wasn't explicitly set by the user
174         if (m_properties.target == QAbstractTexture::TargetAutomatic)
175             m_properties.target = target;
176         m_properties.width = m_textureData->width();
177         m_properties.height = m_textureData->height();
178         m_properties.depth = m_textureData->depth();
179         m_properties.layers = m_textureData->layers();
180         m_properties.format = m_textureData->format();
181 
182         const QVector<QTextureImageDataPtr> imageData = m_textureData->imageData();
183 
184         if (imageData.size() > 0) {
185             // Set the mips level based on the first image if autoMipMapGeneration is disabled
186             if (!m_properties.generateMipMaps)
187                 m_properties.mipLevels = imageData.first()->mipLevels();
188         }
189     }
190     return !m_textureData.isNull();
191 }
192 
loadTextureDataFromImages()193 void GLTexture::loadTextureDataFromImages()
194 {
195     int maxMipLevel = 0;
196     for (const Image &img : qAsConst(m_images)) {
197         const QTextureImageDataPtr imgData = img.generator->operator()();
198         // imgData may be null in the following cases:
199         // - Texture is created with TextureImages which have yet to be
200         // loaded (skybox where you don't yet know the path, source set by
201         // a property binding, queued connection ...)
202         // - TextureImage whose generator failed to return a valid data
203         // (invalid url, error opening file...)
204         if (imgData.isNull())
205             continue;
206 
207         m_imageData.push_back(imgData);
208         maxMipLevel = qMax(maxMipLevel, img.mipLevel);
209 
210         // If the texture doesn't have a texture generator, we will
211         // derive some properties from the first TextureImage (layer=0, miplvl=0, face=0)
212         if (!m_textureData && img.layer == 0 && img.mipLevel == 0 && img.face == QAbstractTexture::CubeMapPositiveX) {
213             if (imgData->width() != -1 && imgData->height() != -1 && imgData->depth() != -1) {
214                 m_properties.width = imgData->width();
215                 m_properties.height = imgData->height();
216                 m_properties.depth = imgData->depth();
217             }
218             // Set the format of the texture if the texture format is set to Automatic
219             if (m_properties.format == QAbstractTexture::Automatic) {
220                 m_properties.format = static_cast<QAbstractTexture::TextureFormat>(imgData->format());
221             }
222             setDirtyFlag(Properties, true);
223         }
224     }
225 
226     // make sure the number of mip levels is set when there is no texture data generator
227     if (!m_dataFunctor) {
228         m_properties.mipLevels = maxMipLevel + 1;
229         setDirtyFlag(Properties, true);
230     }
231 }
232 
233 // Called from RenderThread
createOrUpdateGLTexture()234 GLTexture::TextureUpdateInfo GLTexture::createOrUpdateGLTexture()
235 {
236     TextureUpdateInfo textureInfo;
237     m_wasTextureRecreated = false;
238 
239     const bool hasSharedTextureId = m_sharedTextureId > 0;
240     // Only load texture data if we are not using a sharedTextureId
241     // Check if dataFunctor or images have changed
242     if (!hasSharedTextureId) {
243         // If dataFunctor exists and we have no data and it hasn´t run yet
244         if (m_dataFunctor && !m_textureData && m_dataFunctor.get() != m_pendingDataFunctor ) {
245             const bool successfullyLoadedTextureData = loadTextureDataFromGenerator();
246             // If successful, m_textureData has content
247             if (successfullyLoadedTextureData) {
248                 setDirtyFlag(Properties, true);
249                 setDirtyFlag(TextureData, true);
250             } else {
251                 if (m_pendingDataFunctor != m_dataFunctor.get()) {
252                     qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame";
253                     m_pendingDataFunctor = m_dataFunctor.get();
254                 }
255                 textureInfo.properties.status = QAbstractTexture::Loading;
256                 return textureInfo;
257             }
258         }
259 
260         // If images have changed, clear previous images data
261         // and regenerate m_imageData for the images
262         if (testDirtyFlag(TextureImageData)) {
263             m_imageData.clear();
264             loadTextureDataFromImages();
265             // Mark for upload if we actually have something to upload
266             if (!m_imageData.empty()) {
267                 setDirtyFlag(TextureData, true);
268             }
269             // Reset image flag
270             setDirtyFlag(TextureImageData, false);
271         }
272 
273         // Don't try to create the texture if the target or format was still not set
274         // Format should either be set by user or if Automatic
275         // by either the dataGenerator of the texture or the first Image
276         // Target should explicitly be set by the user or the dataGenerator
277         if (m_properties.target == QAbstractTexture::TargetAutomatic ||
278             m_properties.format == QAbstractTexture::Automatic ||
279             m_properties.format == QAbstractTexture::NoFormat) {
280             textureInfo.properties.status = QAbstractTexture::Error;
281             return textureInfo;
282         }
283     }
284 
285     // If the properties changed or texture has become a shared texture from a
286     // 3rd party engine, we need to destroy and maybe re-allocate the texture
287     if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) {
288         delete m_gl;
289         m_gl = nullptr;
290         textureInfo.wasUpdated = true;
291         // If we are destroyed because of some property change but still have (some) of
292         // our content data make sure we are marked for upload
293         // TO DO: We should actually check if the textureData is still correct
294         // in regard to the size, target and format of the texture though.
295         if (!testDirtyFlag(SharedTextureId) &&
296             (m_textureData || !m_imageData.empty() || !m_pendingTextureDataUpdates.empty()))
297             setDirtyFlag(TextureData, true);
298     }
299 
300     m_properties.status = QAbstractTexture::Ready;
301 
302     if (testDirtyFlag(SharedTextureId) || hasSharedTextureId) {
303         // Update m_properties by doing introspection on the texture
304         if (hasSharedTextureId)
305             introspectPropertiesFromSharedTextureId();
306         setDirtyFlag(SharedTextureId, false);
307     } else {
308         // We only build a QOpenGLTexture if we have no shared textureId set
309         if (!m_gl) {
310             m_gl = buildGLTexture();
311             if (!m_gl) {
312                 qWarning() << "[Qt3DRender::GLTexture] failed to create texture";
313                 textureInfo.properties.status = QAbstractTexture::Error;
314                 return textureInfo;
315             }
316 
317             m_gl->allocateStorage();
318             if (!m_gl->isStorageAllocated()) {
319                 qWarning() << "[Qt3DRender::GLTexture] failed to allocate texture";
320                 textureInfo.properties.status = QAbstractTexture::Error;
321                 return textureInfo;
322             }
323             m_wasTextureRecreated = true;
324         }
325 
326         textureInfo.texture = m_gl;
327 
328         // need to (re-)upload texture data?
329         const bool needsUpload = testDirtyFlag(TextureData);
330         if (needsUpload) {
331             uploadGLTextureData();
332             setDirtyFlag(TextureData, false);
333         }
334 
335         // need to set texture parameters?
336         if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
337             updateGLTextureParameters();
338             setDirtyFlag(Properties, false);
339             setDirtyFlag(Parameters, false);
340         }
341     }
342 
343     textureInfo.properties = m_properties;
344 
345     return textureInfo;
346 }
347 
getOrCreateRenderBuffer()348 RenderBuffer *GLTexture::getOrCreateRenderBuffer()
349 {
350     if (m_dataFunctor && !m_textureData) {
351         m_textureData = m_dataFunctor->operator()();
352         if (m_textureData) {
353             if (m_properties.target != QAbstractTexture::TargetAutomatic)
354                 qWarning() << "[Qt3DRender::GLTexture] [renderbuffer] When a texture provides a generator, it's target is expected to be TargetAutomatic";
355 
356             m_properties.width = m_textureData->width();
357             m_properties.height = m_textureData->height();
358             m_properties.format = m_textureData->format();
359 
360             setDirtyFlag(Properties);
361         } else {
362             if (m_pendingDataFunctor != m_dataFunctor.get()) {
363                 qWarning() << "[Qt3DRender::GLTexture] [renderbuffer] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame";
364                 m_pendingDataFunctor = m_dataFunctor.get();
365             }
366             return nullptr;
367         }
368     }
369 
370     if (testDirtyFlag(Properties)) {
371         delete m_renderBuffer;
372         m_renderBuffer = nullptr;
373     }
374 
375     if (!m_renderBuffer)
376         m_renderBuffer = new RenderBuffer(m_properties.width, m_properties.height, m_properties.format);
377 
378     setDirtyFlag(Properties, false);
379     setDirtyFlag(Parameters, false);
380 
381     return m_renderBuffer;
382 }
383 
384 // This must be called from the RenderThread
385 // So GLTexture release from the manager can only be done from that thread
cleanup()386 void GLTexture::cleanup()
387 {
388     destroy();
389 }
390 
setParameters(const TextureParameters & params)391 void GLTexture::setParameters(const TextureParameters &params)
392 {
393     if (m_parameters != params) {
394         m_parameters = params;
395         setDirtyFlag(Parameters);
396     }
397 }
398 
setProperties(const TextureProperties & props)399 void GLTexture::setProperties(const TextureProperties &props)
400 {
401     if (m_properties != props) {
402         m_properties = props;
403         setDirtyFlag(Properties);
404     }
405 }
406 
setImages(const QVector<Image> & images)407 void GLTexture::setImages(const QVector<Image> &images)
408 {
409     // check if something has changed at all
410     bool same = (images.size() == m_images.size());
411     if (same) {
412         for (int i = 0; i < images.size(); i++) {
413             if (images[i] != m_images[i]) {
414                 same = false;
415                 break;
416             }
417         }
418     }
419 
420 
421     if (!same) {
422         m_images = images;
423         requestImageUpload();
424     }
425 }
426 
setGenerator(const QTextureGeneratorPtr & generator)427 void GLTexture::setGenerator(const QTextureGeneratorPtr &generator)
428 {
429     m_textureData.reset();
430     m_dataFunctor = generator;
431     m_pendingDataFunctor = nullptr;
432     requestUpload();
433 }
434 
setSharedTextureId(int textureId)435 void GLTexture::setSharedTextureId(int textureId)
436 {
437     if (m_sharedTextureId != textureId) {
438         m_sharedTextureId = textureId;
439         setDirtyFlag(SharedTextureId);
440     }
441 }
442 
addTextureDataUpdates(const QVector<QTextureDataUpdate> & updates)443 void GLTexture::addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates)
444 {
445     m_pendingTextureDataUpdates += updates;
446     requestUpload();
447 }
448 
449 // Return nullptr if
450 // - context cannot be obtained
451 // - texture hasn't yet been loaded
buildGLTexture()452 QOpenGLTexture *GLTexture::buildGLTexture()
453 {
454     QOpenGLContext *ctx = QOpenGLContext::currentContext();
455     if (!ctx) {
456         qWarning() << Q_FUNC_INFO << "requires an OpenGL context";
457         return nullptr;
458     }
459 
460     const QAbstractTexture::Target actualTarget = m_properties.target;
461     if (actualTarget == QAbstractTexture::TargetAutomatic) {
462         // If the target is automatic at this point, it means that the texture
463         // hasn't been loaded yet (case of remote urls) and that loading failed
464         // and that target format couldn't be deduced
465         return nullptr;
466     }
467 
468     QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(actualTarget));
469 
470     // m_format may not be ES2 compatible. Now it's time to convert it, if necessary.
471     QAbstractTexture::TextureFormat format = m_properties.format;
472     if (ctx->isOpenGLES() && ctx->format().majorVersion() < 3) {
473         switch (m_properties.format) {
474         case QAbstractTexture::RGBA8_UNorm:
475         case QAbstractTexture::RGBAFormat:
476             format = QAbstractTexture::RGBAFormat;
477             break;
478         case QAbstractTexture::RGB8_UNorm:
479         case QAbstractTexture::RGBFormat:
480             format = QAbstractTexture::RGBFormat;
481             break;
482         case QAbstractTexture::DepthFormat:
483             format = QAbstractTexture::DepthFormat;
484             break;
485         default:
486             auto warning = qWarning();
487             warning << "Could not find a matching OpenGL ES 2.0 texture format:";
488             QtDebugUtils::formatQEnum(warning, m_properties.format);
489             break;
490         }
491     }
492 
493     // Map ETC1 to ETC2 when supported. This allows using features like
494     // immutable storage as ETC2 is standard in GLES 3.0, while the ETC1 extension
495     // is written against GLES 1.0.
496     if (m_properties.format == QAbstractTexture::RGB8_ETC1) {
497         if ((ctx->isOpenGLES() && ctx->format().majorVersion() >= 3)
498             || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture"))
499             || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility")))
500             format = m_properties.format = QAbstractTexture::RGB8_ETC2;
501     }
502 
503     glTex->setFormat(m_properties.format == QAbstractTexture::Automatic ?
504                      QOpenGLTexture::NoFormat :
505                      static_cast<QOpenGLTexture::TextureFormat>(format));
506     glTex->setSize(m_properties.width, m_properties.height, m_properties.depth);
507     // Set layers count if texture array
508     if (actualTarget == QAbstractTexture::Target1DArray ||
509         actualTarget == QAbstractTexture::Target2DArray ||
510         actualTarget == QAbstractTexture::Target2DMultisampleArray ||
511         actualTarget == QAbstractTexture::TargetCubeMapArray) {
512         glTex->setLayers(m_properties.layers);
513     }
514 
515     if (actualTarget == QAbstractTexture::Target2DMultisample ||
516         actualTarget == QAbstractTexture::Target2DMultisampleArray) {
517         // Set samples count if multisampled texture
518         // (multisampled textures don't have mipmaps)
519         glTex->setSamples(m_properties.samples);
520     } else if (m_properties.generateMipMaps) {
521         glTex->setMipLevels(glTex->maximumMipLevels());
522     } else {
523         glTex->setAutoMipMapGenerationEnabled(false);
524         if (glTex->hasFeature(QOpenGLTexture::TextureMipMapLevel)) {
525             glTex->setMipBaseLevel(0);
526             glTex->setMipMaxLevel(m_properties.mipLevels - 1);
527         }
528         glTex->setMipLevels(m_properties.mipLevels);
529     }
530 
531     if (!glTex->create()) {
532         qWarning() << Q_FUNC_INFO << "creating QOpenGLTexture failed";
533         return nullptr;
534     }
535 
536     return glTex;
537 }
538 
uploadGLTextureData()539 void GLTexture::uploadGLTextureData()
540 {
541     // Upload all QTexImageData set by the QTextureGenerator
542     if (m_textureData) {
543         const QVector<QTextureImageDataPtr> imgData = m_textureData->imageData();
544 
545         for (const QTextureImageDataPtr &data : imgData) {
546             const int mipLevels = m_properties.generateMipMaps ? 1 : data->mipLevels();
547 
548             for (int layer = 0; layer < data->layers(); layer++) {
549                 for (int face = 0; face < data->faces(); face++) {
550                     for (int level = 0; level < mipLevels; level++) {
551                         // ensure we don't accidentally cause a detach / copy of the raw bytes
552                         const QByteArray bytes(data->data(layer, face, level));
553                         uploadGLData(m_gl, level, layer,
554                                      static_cast<QOpenGLTexture::CubeMapFace>(QOpenGLTexture::CubeMapPositiveX + face),
555                                      bytes, data);
556                     }
557                 }
558             }
559         }
560     }
561 
562     // Upload all QTexImageData references by the TextureImages
563     for (int i = 0; i < std::min(m_images.size(), m_imageData.size()); i++) {
564         const QTextureImageDataPtr &imgData = m_imageData.at(i);
565         // Here the bytes in the QTextureImageData contain data for a single
566         // layer, face or mip level, unlike the QTextureGenerator case where
567         // they are in a single blob. Hence QTextureImageData::data() is not suitable.
568         const QByteArray bytes(QTextureImageDataPrivate::get(imgData.get())->m_data);
569         uploadGLData(m_gl, m_images[i].mipLevel, m_images[i].layer,
570                      static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face),
571                      bytes, imgData);
572     }
573     // Free up image data once content has been uploaded
574     // Note: if data functor stores the data, this won't really free anything though
575     m_imageData.clear();
576 
577     // Update data from TextureUpdates
578     const QVector<QTextureDataUpdate> textureDataUpdates = std::move(m_pendingTextureDataUpdates);
579     for (const QTextureDataUpdate &update : textureDataUpdates) {
580         const QTextureImageDataPtr imgData = update.data();
581 
582         if (!imgData) {
583             qWarning() << Q_FUNC_INFO << "QTextureDataUpdate no QTextureImageData set";
584             continue;
585         }
586 
587         const int xOffset = update.x();
588         const int yOffset = update.y();
589         const int zOffset = update.z();
590         const int xExtent = xOffset + imgData->width();
591         const int yExtent = yOffset + imgData->height();
592         const int zExtent = zOffset + imgData->depth();
593 
594         // Check update is compatible with our texture
595         if (xOffset >= m_gl->width() ||
596             yOffset >= m_gl->height() ||
597             zOffset >= m_gl->depth() ||
598             xExtent > m_gl->width() ||
599             yExtent > m_gl->height() ||
600             zExtent > m_gl->depth() ||
601             update.mipLevel() >= m_gl->mipLevels() ||
602             update.layer() >= m_gl->layers()) {
603             qWarning() << Q_FUNC_INFO << "QTextureDataUpdate incompatible with texture";
604             continue;
605         }
606 
607         const QByteArray bytes = (QTextureImageDataPrivate::get(imgData.get())->m_data);
608         // Here the bytes in the QTextureImageData contain data for a single
609         // layer, face or mip level, unlike the QTextureGenerator case where
610         // they are in a single blob. Hence QTextureImageData::data() is not suitable.
611 
612         // Check if this is a full sized update
613         if (xOffset == 0 &&
614             yOffset == 0 &&
615             zOffset == 0 &&
616             xExtent == m_gl->width() &&
617             yExtent == m_gl->height() &&
618             zExtent == m_gl->depth()) {
619             uploadGLData(m_gl, update.mipLevel(), update.layer(),
620                          static_cast<QOpenGLTexture::CubeMapFace>(update.face()),
621                          bytes, imgData);
622         } else {
623             if (imgData->isCompressed()) {
624                 qWarning() << Q_FUNC_INFO << "Uploading non full sized Compressed Data not supported yet";
625             } else {
626 
627                 uploadGLData(m_gl,
628                              update.mipLevel(), update.layer(),
629                              static_cast<QOpenGLTexture::CubeMapFace>(update.face()),
630                              xOffset, yOffset, zOffset,
631                              bytes, imgData);
632             }
633         }
634     }
635 }
636 
updateGLTextureParameters()637 void GLTexture::updateGLTextureParameters()
638 {
639     const QAbstractTexture::Target actualTarget = m_properties.target;
640     const bool isMultisampledTexture = (actualTarget == QAbstractTexture::Target2DMultisample ||
641                                         actualTarget == QAbstractTexture::Target2DMultisampleArray);
642     // Multisampled textures can only be accessed by texelFetch in shaders
643     // and don't support wrap modes and mig/mag filtes
644     if (isMultisampledTexture)
645         return;
646 
647     m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX));
648     if (actualTarget != QAbstractTexture::Target1D &&
649         actualTarget != QAbstractTexture::Target1DArray &&
650         actualTarget != QAbstractTexture::TargetBuffer)
651         m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY));
652     if (actualTarget == QAbstractTexture::Target3D)
653         m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ));
654     m_gl->setMinMagFilters(static_cast<QOpenGLTexture::Filter>(m_parameters.minificationFilter),
655                            static_cast<QOpenGLTexture::Filter>(m_parameters.magnificationFilter));
656     if (m_gl->hasFeature(QOpenGLTexture::AnisotropicFiltering))
657         m_gl->setMaximumAnisotropy(m_parameters.maximumAnisotropy);
658     if (m_gl->hasFeature(QOpenGLTexture::TextureComparisonOperators)) {
659         m_gl->setComparisonFunction(static_cast<QOpenGLTexture::ComparisonFunction>(m_parameters.comparisonFunction));
660         m_gl->setComparisonMode(static_cast<QOpenGLTexture::ComparisonMode>(m_parameters.comparisonMode));
661     }
662 }
663 
introspectPropertiesFromSharedTextureId()664 void GLTexture::introspectPropertiesFromSharedTextureId()
665 {
666     // We know that the context is active when this function is called
667     QOpenGLContext *ctx = QOpenGLContext::currentContext();
668     if (!ctx) {
669         qWarning() << Q_FUNC_INFO << "requires an OpenGL context";
670         return;
671     }
672     QOpenGLFunctions *gl = ctx->functions();
673 
674     // If the user has set the target format himself, we won't try to deduce it
675     if (m_properties.target != QAbstractTexture::TargetAutomatic)
676         return;
677 
678     const QAbstractTexture::Target targets[] = {
679         QAbstractTexture::Target2D,
680         QAbstractTexture::TargetCubeMap,
681 #ifndef QT_OPENGL_ES_2
682         QAbstractTexture::Target1D,
683         QAbstractTexture::Target1DArray,
684         QAbstractTexture::Target3D,
685         QAbstractTexture::Target2DArray,
686         QAbstractTexture::TargetCubeMapArray,
687         QAbstractTexture::Target2DMultisample,
688         QAbstractTexture::Target2DMultisampleArray,
689         QAbstractTexture::TargetRectangle,
690         QAbstractTexture::TargetBuffer,
691 #endif
692     };
693 
694 #ifndef QT_OPENGL_ES_2
695     // Try to find texture target with GL 4.5 functions
696     const QPair<int, int> ctxGLVersion = ctx->format().version();
697     if (ctxGLVersion.first > 4 || (ctxGLVersion.first == 4 && ctxGLVersion.second >= 5)) {
698         // Only for GL 4.5+
699 #ifdef GL_TEXTURE_TARGET
700         QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>();
701         if (gl5 != nullptr)
702             gl5->glGetTextureParameteriv(m_sharedTextureId, GL_TEXTURE_TARGET, reinterpret_cast<int *>(&m_properties.target));
703 #endif
704     }
705 #endif
706 
707     // If GL 4.5 function unavailable or not working, try a slower way
708     if (m_properties.target == QAbstractTexture::TargetAutomatic) {
709         //    // OpenGL offers no proper way of querying for the target of a texture given its id
710         gl->glActiveTexture(GL_TEXTURE0);
711 
712         const GLenum targetBindings[] = {
713             GL_TEXTURE_BINDING_2D,
714             GL_TEXTURE_BINDING_CUBE_MAP,
715 #ifndef QT_OPENGL_ES_2
716             GL_TEXTURE_BINDING_1D,
717             GL_TEXTURE_BINDING_1D_ARRAY,
718             GL_TEXTURE_BINDING_3D,
719             GL_TEXTURE_BINDING_2D_ARRAY,
720             GL_TEXTURE_BINDING_CUBE_MAP_ARRAY,
721             GL_TEXTURE_BINDING_2D_MULTISAMPLE,
722             GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,
723             GL_TEXTURE_BINDING_RECTANGLE,
724             GL_TEXTURE_BINDING_BUFFER
725 #endif
726         };
727 
728         Q_STATIC_ASSERT(sizeof(targetBindings) / sizeof(targetBindings[0]) == sizeof(targets) / sizeof(targets[0]));
729 
730         for (uint i = 0; i < sizeof(targetBindings) / sizeof(targetBindings[0]); ++i) {
731             const int target = targets[i];
732             gl->glBindTexture(target, m_sharedTextureId);
733             int boundId = 0;
734             gl->glGetIntegerv(targetBindings[i], &boundId);
735             gl->glBindTexture(target, 0);
736             if (boundId == m_sharedTextureId) {
737                 m_properties.target = static_cast<QAbstractTexture::Target>(target);
738                 break;
739             }
740         }
741     }
742 
743     // Return early if we weren't able to find texture target
744     if (std::find(std::begin(targets), std::end(targets), m_properties.target) == std::end(targets)) {
745         qWarning() << "Unable to determine texture target for shared GL texture";
746         return;
747     }
748 
749     // Bind texture once we know its target
750     gl->glBindTexture(m_properties.target, m_sharedTextureId);
751 
752     // TO DO: Improve by using glGetTextureParameters when available which
753     // support direct state access
754 #ifndef GL_TEXTURE_MAX_LEVEL
755 #define GL_TEXTURE_MAX_LEVEL        0x813D
756 #endif
757 
758 #ifndef GL_TEXTURE_WRAP_R
759 #define GL_TEXTURE_WRAP_R           0x8072
760 #endif
761 
762     gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAX_LEVEL, reinterpret_cast<int *>(&m_properties.mipLevels));
763     gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MIN_FILTER, reinterpret_cast<int *>(&m_parameters.minificationFilter));
764     gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAG_FILTER, reinterpret_cast<int *>(&m_parameters.magnificationFilter));
765     gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_R, reinterpret_cast<int *>(&m_parameters.wrapModeX));
766     gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_S, reinterpret_cast<int *>(&m_parameters.wrapModeY));
767     gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_T, reinterpret_cast<int *>(&m_parameters.wrapModeZ));
768 
769 #ifndef QT_OPENGL_ES_2
770     // Try to retrieve dimensions (not available on ES 2.0)
771     if (!ctx->isOpenGLES()) {
772         QOpenGLFunctions_3_1 *gl3 = ctx->versionFunctions<QOpenGLFunctions_3_1>();
773         if (!gl3) {
774             qWarning() << "Failed to retrieve shared texture dimensions";
775             return;
776         }
777 
778         gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_WIDTH, reinterpret_cast<int *>(&m_properties.width));
779         gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_HEIGHT, reinterpret_cast<int *>(&m_properties.height));
780         gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_DEPTH, reinterpret_cast<int *>(&m_properties.depth));
781         gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<int *>(&m_properties.format));
782     }
783 #endif
784 
785     gl->glBindTexture(m_properties.target, 0);
786 }
787 
788 } // namespace OpenGL
789 } // namespace Render
790 } // namespace Qt3DRender
791 
792 QT_END_NAMESPACE
793