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