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
63QT_BEGIN_NAMESPACE
64
65using namespace Qt3DCore;
66
67namespace Qt3DRender {
68namespace Render {
69namespace OpenGL {
70
71namespace {
72
73// This uploadGLData where the data is a fullsize subimage
74// as QOpenGLTexture doesn't allow partial subimage uploads
75void 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(imageData: data.get())->m_alignment;
80 QOpenGLPixelTransferOptions uploadOptions;
81 uploadOptions.setAlignment(alignment);
82 if (data->isCompressed())
83 glTex->setCompressedData(mipLevel: level, layer, cubeFace: face, dataSize: bytes.size(), data: bytes.constData(), options: &uploadOptions);
84 else
85 glTex->setData(mipLevel: level, layer, cubeFace: face, sourceFormat: data->pixelFormat(), sourceType: data->pixelType(), data: bytes.constData(), options: &uploadOptions);
86}
87
88// For partial sub image uploads
89void 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(imageData: data.get())->m_alignment;
98 QOpenGLPixelTransferOptions uploadOptions;
99 uploadOptions.setAlignment(alignment);
100 glTex->setData(xOffset, yOffset, zOffset,
101 width: data->width(), height: data->height(), depth: data->depth(),
102 mipLevel, layer, cubeFace, layerCount: data->layers(),
103 sourceFormat: data->pixelFormat(), sourceType: data->pixelType(),
104 data: bytes.constData(), options: &uploadOptions);
105 }
106}
107
108} // anonymous
109
110
111GLTexture::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
123GLTexture::~GLTexture()
124{
125}
126
127// Must be called from RenderThread with active GL context
128void 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
150bool 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
193void GLTexture::loadTextureDataFromImages()
194{
195 int maxMipLevel = 0;
196 for (const Image &img : qAsConst(t&: 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(t: imgData);
208 maxMipLevel = qMax(a: maxMipLevel, b: 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(flag: Properties, value: 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(flag: Properties, value: true);
230 }
231}
232
233// Called from RenderThread
234GLTexture::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(flag: Properties, value: true);
249 setDirtyFlag(flag: TextureData, value: 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(flag: 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(flag: TextureData, value: true);
268 }
269 // Reset image flag
270 setDirtyFlag(flag: TextureImageData, value: 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(flag: Properties) || testDirtyFlag(flag: 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(flag: SharedTextureId) &&
296 (m_textureData || !m_imageData.empty() || !m_pendingTextureDataUpdates.empty()))
297 setDirtyFlag(flag: TextureData, value: true);
298 }
299
300 m_properties.status = QAbstractTexture::Ready;
301
302 if (testDirtyFlag(flag: SharedTextureId) || hasSharedTextureId) {
303 // Update m_properties by doing introspection on the texture
304 if (hasSharedTextureId)
305 introspectPropertiesFromSharedTextureId();
306 setDirtyFlag(flag: SharedTextureId, value: 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(flag: TextureData);
330 if (needsUpload) {
331 uploadGLTextureData();
332 setDirtyFlag(flag: TextureData, value: false);
333 }
334
335 // need to set texture parameters?
336 if (testDirtyFlag(flag: Properties) || testDirtyFlag(flag: Parameters)) {
337 updateGLTextureParameters();
338 setDirtyFlag(flag: Properties, value: false);
339 setDirtyFlag(flag: Parameters, value: false);
340 }
341 }
342
343 textureInfo.properties = m_properties;
344
345 return textureInfo;
346}
347
348RenderBuffer *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(flag: 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(flag: 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(flag: Properties, value: false);
379 setDirtyFlag(flag: Parameters, value: 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
386void GLTexture::cleanup()
387{
388 destroy();
389}
390
391void GLTexture::setParameters(const TextureParameters &params)
392{
393 if (m_parameters != params) {
394 m_parameters = params;
395 setDirtyFlag(flag: Parameters);
396 }
397}
398
399void GLTexture::setProperties(const TextureProperties &props)
400{
401 if (m_properties != props) {
402 m_properties = props;
403 setDirtyFlag(flag: Properties);
404 }
405}
406
407void 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
427void GLTexture::setGenerator(const QTextureGeneratorPtr &generator)
428{
429 m_textureData.reset();
430 m_dataFunctor = generator;
431 m_pendingDataFunctor = nullptr;
432 requestUpload();
433}
434
435void GLTexture::setSharedTextureId(int textureId)
436{
437 if (m_sharedTextureId != textureId) {
438 m_sharedTextureId = textureId;
439 setDirtyFlag(flag: SharedTextureId);
440 }
441}
442
443void 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
452QOpenGLTexture *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(debug&: warning, value: 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(width: m_properties.width, height: m_properties.height, depth: 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(feature: 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
539void 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, mipmapLevel: level));
553 uploadGLData(glTex: m_gl, level, layer,
554 face: 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(a: m_images.size(), b: 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(imageData: imgData.get())->m_data);
569 uploadGLData(glTex: m_gl, level: m_images[i].mipLevel, layer: m_images[i].layer,
570 face: static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face),
571 bytes, data: 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(imageData: 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(glTex: m_gl, level: update.mipLevel(), layer: update.layer(),
620 face: static_cast<QOpenGLTexture::CubeMapFace>(update.face()),
621 bytes, data: 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(glTex: m_gl,
628 mipLevel: update.mipLevel(), layer: update.layer(),
629 cubeFace: static_cast<QOpenGLTexture::CubeMapFace>(update.face()),
630 xOffset, yOffset, zOffset,
631 bytes, data: imgData);
632 }
633 }
634 }
635}
636
637void 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(direction: QOpenGLTexture::DirectionS, mode: static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX));
648 if (actualTarget != QAbstractTexture::Target1D &&
649 actualTarget != QAbstractTexture::Target1DArray &&
650 actualTarget != QAbstractTexture::TargetBuffer)
651 m_gl->setWrapMode(direction: QOpenGLTexture::DirectionT, mode: static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY));
652 if (actualTarget == QAbstractTexture::Target3D)
653 m_gl->setWrapMode(direction: QOpenGLTexture::DirectionR, mode: static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ));
654 m_gl->setMinMagFilters(minificationFilter: static_cast<QOpenGLTexture::Filter>(m_parameters.minificationFilter),
655 magnificationFilter: static_cast<QOpenGLTexture::Filter>(m_parameters.magnificationFilter));
656 if (m_gl->hasFeature(feature: QOpenGLTexture::AnisotropicFiltering))
657 m_gl->setMaximumAnisotropy(m_parameters.maximumAnisotropy);
658 if (m_gl->hasFeature(feature: 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
664void 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(texture: m_sharedTextureId, GL_TEXTURE_TARGET, params: 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, texture: m_sharedTextureId);
733 int boundId = 0;
734 gl->glGetIntegerv(pname: targetBindings[i], params: &boundId);
735 gl->glBindTexture(target, texture: 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(first: std::begin(arr: targets), last: std::end(arr: targets), val: m_properties.target) == std::end(arr: 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(target: m_properties.target, texture: 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(target: int(m_properties.target), GL_TEXTURE_MAX_LEVEL, params: reinterpret_cast<int *>(&m_properties.mipLevels));
763 gl->glGetTexParameteriv(target: int(m_properties.target), GL_TEXTURE_MIN_FILTER, params: reinterpret_cast<int *>(&m_parameters.minificationFilter));
764 gl->glGetTexParameteriv(target: int(m_properties.target), GL_TEXTURE_MAG_FILTER, params: reinterpret_cast<int *>(&m_parameters.magnificationFilter));
765 gl->glGetTexParameteriv(target: int(m_properties.target), GL_TEXTURE_WRAP_R, params: reinterpret_cast<int *>(&m_parameters.wrapModeX));
766 gl->glGetTexParameteriv(target: int(m_properties.target), GL_TEXTURE_WRAP_S, params: reinterpret_cast<int *>(&m_parameters.wrapModeY));
767 gl->glGetTexParameteriv(target: int(m_properties.target), GL_TEXTURE_WRAP_T, params: 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(target: int(m_properties.target), level: 0, GL_TEXTURE_WIDTH, params: reinterpret_cast<int *>(&m_properties.width));
779 gl3->glGetTexLevelParameteriv(target: int(m_properties.target), level: 0, GL_TEXTURE_HEIGHT, params: reinterpret_cast<int *>(&m_properties.height));
780 gl3->glGetTexLevelParameteriv(target: int(m_properties.target), level: 0, GL_TEXTURE_DEPTH, params: reinterpret_cast<int *>(&m_properties.depth));
781 gl3->glGetTexLevelParameteriv(target: int(m_properties.target), level: 0, GL_TEXTURE_INTERNAL_FORMAT, params: reinterpret_cast<int *>(&m_properties.format));
782 }
783#endif
784
785 gl->glBindTexture(target: m_properties.target, texture: 0);
786}
787
788} // namespace OpenGL
789} // namespace Render
790} // namespace Qt3DRender
791
792QT_END_NAMESPACE
793

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