1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQuick 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 
41 #include "qsgcompressedtexture_p.h"
42 #include <QOpenGLContext>
43 #include <QOpenGLTexture>
44 #include <QOpenGLFunctions>
45 #include <QDebug>
46 #include <QtQuick/private/qquickwindow_p.h>
47 #include <QtGui/private/qrhi_p.h>
48 
49 QT_BEGIN_NAMESPACE
50 
51 Q_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO, "qt.scenegraph.textureio");
52 
QSGCompressedTexture(const QTextureFileData & texData)53 QSGCompressedTexture::QSGCompressedTexture(const QTextureFileData &texData)
54     : QSGTexture(*(new QSGCompressedTexturePrivate)),
55       m_textureData(texData)
56 {
57     m_size = m_textureData.size();
58     m_hasAlpha = !formatIsOpaque(m_textureData.glInternalFormat());
59 }
60 
~QSGCompressedTexture()61 QSGCompressedTexture::~QSGCompressedTexture()
62 {
63 #if QT_CONFIG(opengl)
64     if (m_textureId) {
65         QOpenGLContext *ctx = QOpenGLContext::currentContext();
66         QOpenGLFunctions *funcs = ctx ? ctx->functions() : nullptr;
67         if (!funcs)
68             return;
69 
70         funcs->glDeleteTextures(1, &m_textureId);
71     }
72 #endif
73 
74     delete m_texture;
75 }
76 
textureId() const77 int QSGCompressedTexture::textureId() const
78 {
79 #if QT_CONFIG(opengl)
80     if (!m_textureId) {
81         QOpenGLContext *ctx = QOpenGLContext::currentContext();
82         QOpenGLFunctions *funcs = ctx ? ctx->functions() : nullptr;
83         if (!funcs)
84             return 0;
85 
86         funcs->glGenTextures(1, &m_textureId);
87     }
88 #endif
89     return m_textureId;
90 }
91 
comparisonKey() const92 int QSGCompressedTexturePrivate::comparisonKey() const
93 {
94     Q_Q(const QSGCompressedTexture);
95     // not textureId() as that would create an id when not yet done - that's not wanted here
96     if (q->m_textureId)
97         return q->m_textureId;
98 
99     if (q->m_texture)
100         return int(qintptr(q->m_texture));
101 
102     // two textures (and so materials) with not-yet-created texture underneath are never equal
103     return int(qintptr(q));
104 }
105 
textureSize() const106 QSize QSGCompressedTexture::textureSize() const
107 {
108     return m_size;
109 }
110 
hasAlphaChannel() const111 bool QSGCompressedTexture::hasAlphaChannel() const
112 {
113     return m_hasAlpha;
114 }
115 
hasMipmaps() const116 bool QSGCompressedTexture::hasMipmaps() const
117 {
118     return false;
119 }
120 
bind()121 void QSGCompressedTexture::bind()
122 {
123 #if QT_CONFIG(opengl)
124     QOpenGLContext *ctx = QOpenGLContext::currentContext();
125     QOpenGLFunctions *funcs = ctx ? ctx->functions() : nullptr;
126     if (!funcs)
127         return;
128 
129     if (!textureId())
130         return;
131 
132     funcs->glBindTexture(GL_TEXTURE_2D, m_textureId);
133 
134     if (m_uploaded)
135         return;
136 
137     if (!m_textureData.isValid()) {
138         qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", m_textureData.logName().constData());
139         funcs->glBindTexture(GL_TEXTURE_2D, 0);
140         return;
141     }
142 
143     if (Q_UNLIKELY(QSG_LOG_TEXTUREIO().isDebugEnabled())) {
144         qCDebug(QSG_LOG_TEXTUREIO) << "Uploading texture" << m_textureData;
145         while (funcs->glGetError() != GL_NO_ERROR);
146     }
147 
148     funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_textureData.glInternalFormat(),
149                                   m_size.width(), m_size.height(), 0, m_textureData.dataLength(),
150                                   m_textureData.data().constData() + m_textureData.dataOffset());
151 
152     if (Q_UNLIKELY(QSG_LOG_TEXTUREIO().isDebugEnabled())) {
153         GLuint error = funcs->glGetError();
154         if (error != GL_NO_ERROR) {
155             qCDebug(QSG_LOG_TEXTUREIO, "glCompressedTexImage2D failed for %s, error 0x%x", m_textureData.logName().constData(), error);
156         }
157     }
158 
159     m_textureData = QTextureFileData();  // Release this memory, not needed anymore
160 
161     updateBindOptions(true);
162     m_uploaded = true;
163 #endif // QT_CONFIG(opengl)
164 }
165 
toRhiCompressedFormat(uint glinternalformat)166 static QPair<QRhiTexture::Format, bool> toRhiCompressedFormat(uint glinternalformat)
167 {
168     switch (glinternalformat) {
169     case QOpenGLTexture::RGB_DXT1:
170         return { QRhiTexture::BC1, false };
171     case QOpenGLTexture::SRGB_DXT1:
172         return { QRhiTexture::BC1, true };
173 
174     case QOpenGLTexture::RGBA_DXT3:
175         return { QRhiTexture::BC3, false };
176     case QOpenGLTexture::SRGB_Alpha_DXT3:
177         return { QRhiTexture::BC3, true };
178 
179     case QOpenGLTexture::RGBA_DXT5:
180         return { QRhiTexture::BC5, false };
181     case QOpenGLTexture::SRGB_Alpha_DXT5:
182         return { QRhiTexture::BC5, true };
183 
184     case QOpenGLTexture::RGB8_ETC2:
185         return { QRhiTexture::ETC2_RGB8, false };
186     case QOpenGLTexture::SRGB8_ETC2:
187         return { QRhiTexture::ETC2_RGB8, true };
188 
189     case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
190         return { QRhiTexture::ETC2_RGB8A1, false };
191     case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
192         return { QRhiTexture::ETC2_RGB8A1, true };
193 
194     case QOpenGLTexture::RGBA8_ETC2_EAC:
195         return { QRhiTexture::ETC2_RGBA8, false };
196     case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC:
197         return { QRhiTexture::ETC2_RGBA8, true };
198 
199     case QOpenGLTexture::RGBA_ASTC_4x4:
200         return { QRhiTexture::ASTC_4x4, false };
201     case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4:
202         return { QRhiTexture::ASTC_4x4, true };
203 
204     case QOpenGLTexture::RGBA_ASTC_5x4:
205         return { QRhiTexture::ASTC_5x4, false };
206     case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4:
207         return { QRhiTexture::ASTC_5x4, true };
208 
209     case QOpenGLTexture::RGBA_ASTC_5x5:
210         return { QRhiTexture::ASTC_5x5, false };
211     case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5:
212         return { QRhiTexture::ASTC_5x5, true };
213 
214     case QOpenGLTexture::RGBA_ASTC_6x5:
215         return { QRhiTexture::ASTC_6x5, false };
216     case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5:
217         return { QRhiTexture::ASTC_6x5, true };
218 
219     case QOpenGLTexture::RGBA_ASTC_6x6:
220         return { QRhiTexture::ASTC_6x6, false };
221     case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6:
222         return { QRhiTexture::ASTC_6x6, true };
223 
224     case QOpenGLTexture::RGBA_ASTC_8x5:
225         return { QRhiTexture::ASTC_8x5, false };
226     case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5:
227         return { QRhiTexture::ASTC_8x5, true };
228 
229     case QOpenGLTexture::RGBA_ASTC_8x6:
230         return { QRhiTexture::ASTC_8x6, false };
231     case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6:
232         return { QRhiTexture::ASTC_8x6, true };
233 
234     case QOpenGLTexture::RGBA_ASTC_8x8:
235         return { QRhiTexture::ASTC_8x8, false };
236     case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8:
237         return { QRhiTexture::ASTC_8x8, true };
238 
239     case QOpenGLTexture::RGBA_ASTC_10x5:
240         return { QRhiTexture::ASTC_10x5, false };
241     case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5:
242         return { QRhiTexture::ASTC_10x5, true };
243 
244     case QOpenGLTexture::RGBA_ASTC_10x6:
245         return { QRhiTexture::ASTC_10x6, false };
246     case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6:
247         return { QRhiTexture::ASTC_10x6, true };
248 
249     case QOpenGLTexture::RGBA_ASTC_10x8:
250         return { QRhiTexture::ASTC_10x8, false };
251     case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8:
252         return { QRhiTexture::ASTC_10x8, true };
253 
254     case QOpenGLTexture::RGBA_ASTC_10x10:
255         return { QRhiTexture::ASTC_10x10, false };
256     case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10:
257         return { QRhiTexture::ASTC_10x10, true };
258 
259     case QOpenGLTexture::RGBA_ASTC_12x10:
260         return { QRhiTexture::ASTC_12x10, false };
261     case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10:
262         return { QRhiTexture::ASTC_12x10, true };
263 
264     case QOpenGLTexture::RGBA_ASTC_12x12:
265         return { QRhiTexture::ASTC_12x12, false };
266     case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12:
267         return { QRhiTexture::ASTC_12x12, true };
268 
269     default:
270         return { QRhiTexture::UnknownFormat, false };
271     }
272 }
273 
rhiTexture() const274 QRhiTexture *QSGCompressedTexturePrivate::rhiTexture() const
275 {
276     Q_Q(const QSGCompressedTexture);
277     return q->m_texture;
278 }
279 
updateRhiTexture(QRhi * rhi,QRhiResourceUpdateBatch * resourceUpdates)280 void QSGCompressedTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
281 {
282     Q_Q(QSGCompressedTexture);
283     if (q->m_uploaded)
284         return;
285 
286     q->m_uploaded = true; // even if fails, no point in trying again
287 
288     if (!q->m_textureData.isValid()) {
289         qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", q->m_textureData.logName().constData());
290         return;
291     }
292 
293     const QPair<QRhiTexture::Format, bool> fmt = toRhiCompressedFormat(q->m_textureData.glInternalFormat());
294     if (fmt.first == QRhiTexture::UnknownFormat) {
295         qWarning("Unknown compressed format 0x%x", q->m_textureData.glInternalFormat());
296         return;
297     }
298 
299     QRhiTexture::Flags texFlags;
300     if (fmt.second)
301         texFlags |= QRhiTexture::sRGB;
302 
303     if (!rhi->isTextureFormatSupported(fmt.first, texFlags)) {
304         qWarning("Unsupported compressed format 0x%x", q->m_textureData.glInternalFormat());
305         return;
306     }
307 
308     if (!q->m_texture) {
309         q->m_texture = rhi->newTexture(fmt.first, q->m_size, 1, texFlags);
310         if (!q->m_texture->build()) {
311             qWarning("Failed to create QRhiTexture for compressed data");
312             delete q->m_texture;
313             q->m_texture = nullptr;
314             return;
315         }
316     }
317 
318     // only upload mip level 0 since we never do mipmapping for compressed textures (for now?)
319     resourceUpdates->uploadTexture(q->m_texture, QRhiTextureUploadEntry(0, 0,
320         { q->m_textureData.data().constData() + q->m_textureData.dataOffset(), q->m_textureData.dataLength() }));
321 
322     q->m_textureData = QTextureFileData(); // Release this memory, not needed anymore
323 }
324 
textureData() const325 QTextureFileData QSGCompressedTexture::textureData() const
326 {
327     return m_textureData;
328 }
329 
formatIsOpaque(quint32 glTextureFormat)330 bool QSGCompressedTexture::formatIsOpaque(quint32 glTextureFormat)
331 {
332     switch (glTextureFormat) {
333     case QOpenGLTexture::RGB_DXT1:
334     case QOpenGLTexture::R_ATI1N_UNorm:
335     case QOpenGLTexture::R_ATI1N_SNorm:
336     case QOpenGLTexture::RG_ATI2N_UNorm:
337     case QOpenGLTexture::RG_ATI2N_SNorm:
338     case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT:
339     case QOpenGLTexture::RGB_BP_SIGNED_FLOAT:
340     case QOpenGLTexture::R11_EAC_UNorm:
341     case QOpenGLTexture::R11_EAC_SNorm:
342     case QOpenGLTexture::RG11_EAC_UNorm:
343     case QOpenGLTexture::RG11_EAC_SNorm:
344     case QOpenGLTexture::RGB8_ETC2:
345     case QOpenGLTexture::SRGB8_ETC2:
346     case QOpenGLTexture::RGB8_ETC1:
347     case QOpenGLTexture::SRGB_DXT1:
348         return true;
349         break;
350     default:
351         return false;
352     }
353 }
354 
QSGCompressedTextureFactory(const QTextureFileData & texData)355 QSGCompressedTextureFactory::QSGCompressedTextureFactory(const QTextureFileData &texData)
356     : m_textureData(texData)
357 {
358 }
359 
createTexture(QQuickWindow * window) const360 QSGTexture *QSGCompressedTextureFactory::createTexture(QQuickWindow *window) const
361 {
362     if (!m_textureData.isValid())
363         return nullptr;
364 
365     // attempt to atlas the texture
366     QSGRenderContext *context = QQuickWindowPrivate::get(window)->context;
367     QSGTexture *t = context->compressedTextureForFactory(this);
368     if (t)
369         return t;
370 
371     return new QSGCompressedTexture(m_textureData);
372 }
373 
textureByteCount() const374 int QSGCompressedTextureFactory::textureByteCount() const
375 {
376     return qMax(0, m_textureData.data().size() - m_textureData.dataOffset());
377 }
378 
textureSize() const379 QSize QSGCompressedTextureFactory::textureSize() const
380 {
381     return m_textureData.size();
382 }
383 
384 QT_END_NAMESPACE
385