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

source code of qt3d/src/plugins/renderers/opengl/textures/gltexture.cpp