1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 #include <QtQuick3DRender/private/qssgrendercontext_p.h>
32 #include <QtQuick3DUtils/private/qssgutils_p.h>
33 #include <QtQuick/QSGTexture>
34 
35 QT_BEGIN_NAMESPACE
36 
QSSGRenderTexture2D(const QSSGRef<QSSGRenderContext> & context)37 QSSGRenderTexture2D::QSSGRenderTexture2D(const QSSGRef<QSSGRenderContext> &context)
38     : QSSGRenderTextureBase(context, QSSGRenderTextureTargetType::Texture2D), m_width(0), m_height(0)
39 {
40 }
41 
QSSGRenderTexture2D(const QSSGRef<QSSGRenderContext> & context,QSGTexture * qsgTexture)42 QSSGRenderTexture2D::QSSGRenderTexture2D(const QSSGRef<QSSGRenderContext> &context, QSGTexture *qsgTexture)
43     : QSSGRenderTextureBase(context, QSSGRenderTextureTargetType::Texture2D, false),
44       m_width(qsgTexture->textureSize().width()),
45       m_height(qsgTexture->textureSize().height())
46 {
47     Q_ASSERT(!m_ownsTexture);
48     Q_ASSERT(!m_handle);
49     m_handle = reinterpret_cast<QSSGRenderBackend::QSSGRenderBackendTextureObject>(quintptr(qsgTexture->textureId()));
50     m_texTarget = QSSGRenderTextureTargetType::Texture2D;
51 
52     m_format = qsgTexture->hasAlphaChannel() ? QSSGRenderTextureFormat::RGBA8
53                                              : QSSGRenderTextureFormat::RGB8;
54     m_sampleCount = 1; // TODO
55 }
56 
~QSSGRenderTexture2D()57 QSSGRenderTexture2D::~QSSGRenderTexture2D()
58 {
59 }
60 
textureDetails() const61 QSSGTextureDetails QSSGRenderTexture2D::textureDetails() const
62 {
63     return QSSGTextureDetails(m_width, m_height, 0, m_sampleCount, m_format);
64 }
65 
setTextureData(QSSGByteView newBuffer,quint8 inMipLevel,qint32 width,qint32 height,QSSGRenderTextureFormat format,QSSGRenderTextureFormat formatDest)66 void QSSGRenderTexture2D::setTextureData(QSSGByteView newBuffer,
67                                            quint8 inMipLevel,
68                                            qint32 width,
69                                            qint32 height,
70                                            QSSGRenderTextureFormat format,
71                                            QSSGRenderTextureFormat formatDest)
72 {
73     Q_ASSERT(m_handle);
74 
75     // check if we should compress this texture
76 
77     if (inMipLevel == 0) {
78         m_width = width;
79         m_height = height;
80         m_format = format;
81 
82         // We re-use textures and this might have been a MSAA texture before
83         // for resue we must completely destroy the texture object and create a new one
84         // The same is true for immutable textures
85         if (m_texTarget == QSSGRenderTextureTargetType::Texture2D_MS || m_immutable) {
86             m_backend->releaseTexture(m_handle);
87             m_texTarget = QSSGRenderTextureTargetType::Texture2D;
88             m_sampleCount = 1;
89             m_handle = m_backend->createTexture();
90         }
91 
92         if (formatDest.isCompressedTextureFormat()) {
93             bool compress = format.isUncompressedTextureFormat();
94             bool appropriateSizes = !((width % 4) || (height % 4));
95 
96             // we only compress multiple of 4 textures
97             if (compress && !appropriateSizes)
98                 compress = false;
99 
100             if (compress) {
101                 // This seems like a very dubious line here.  If we are compressing then the
102                 // image
103                 // is really 1/4 the width and height? - CN
104                 m_width = width / 4;
105                 m_height = height / 4;
106                 m_format = formatDest;
107             }
108         } else if (formatDest.isUncompressedTextureFormat()) {
109             m_format = formatDest;
110         }
111     }
112 
113     if (m_maxMipLevel < inMipLevel) {
114         m_maxMipLevel = inMipLevel;
115     }
116 
117     // get max size and check value
118     qint32 maxWidth, maxHeight;
119     m_context->maxTextureSize(maxWidth, maxHeight);
120     if (width > maxWidth || height > maxHeight) {
121         qCCritical(RENDER_INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight);
122     }
123     if (format.isUncompressedTextureFormat() || format.isDepthTextureFormat()) {
124         m_backend->setTextureData2D(m_handle,
125                                     m_texTarget,
126                                     inMipLevel,
127                                     m_format,
128                                     width,
129                                     height,
130                                     0,
131                                     format,
132                                     newBuffer);
133     } else if (format.isCompressedTextureFormat()) {
134         m_backend->setCompressedTextureData2D(m_handle,
135                                               m_texTarget,
136                                               inMipLevel,
137                                               format,
138                                               width,
139                                               height,
140                                               0,
141                                               newBuffer);
142     }
143     // Set our texture parameters to a default that will look the best
144     if (inMipLevel > 0)
145         setMinFilter(QSSGRenderTextureMinifyingOp::LinearMipmapLinear);
146 }
147 
setTextureStorage(qint32 inLevels,qint32 width,qint32 height,QSSGRenderTextureFormat formaInternal,QSSGRenderTextureFormat format,QSSGByteView dataBuffer)148 void QSSGRenderTexture2D::setTextureStorage(qint32 inLevels,
149                                               qint32 width,
150                                               qint32 height,
151                                               QSSGRenderTextureFormat formaInternal,
152                                               QSSGRenderTextureFormat format,
153                                               QSSGByteView dataBuffer)
154 {
155     Q_ASSERT(m_handle);
156 
157     if (!m_context->supportsShaderImageLoadStore()) {
158         qCCritical(RENDER_INVALID_OPERATION, "The extension Shader_Image_Load_Store is not supported");
159         return;
160     }
161 
162     m_width = width;
163     m_height = height;
164     m_format = formaInternal;
165     if (format == QSSGRenderTextureFormat::Unknown)
166         format = formaInternal;
167 
168     // get max size and check value
169     qint32 maxWidth, maxHeight;
170     m_context->maxTextureSize(maxWidth, maxHeight);
171     if (width > maxWidth || height > maxHeight) {
172         qCCritical(RENDER_INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight);
173     }
174 
175     if (inLevels < 1) {
176         qCCritical(RENDER_INVALID_PARAMETER, "inLevels is less than 1 (%d)", inLevels);
177     }
178 
179     m_maxMipLevel = inLevels - 1; // we count from 0
180 
181     // only uncompressed formats are supported and no depth
182     if (formaInternal.isUncompressedTextureFormat()) {
183         m_backend->createTextureStorage2D(m_handle, m_texTarget, inLevels, formaInternal, width, height);
184 
185         m_immutable = true;
186         m_texTarget = QSSGRenderTextureTargetType::Texture2D;
187 
188         if (dataBuffer.size() > 0)
189             m_backend->setTextureSubData2D(m_handle, m_texTarget, 0, 0, 0, width, height, format, dataBuffer);
190 
191         if (inLevels > 1)
192             setMinFilter(QSSGRenderTextureMinifyingOp::LinearMipmapLinear);
193     }
194 }
195 
setTextureDataMultisample(qint32 sampleCount,qint32 width,qint32 height,QSSGRenderTextureFormat format)196 void QSSGRenderTexture2D::setTextureDataMultisample(qint32 sampleCount,
197                                                       qint32 width,
198                                                       qint32 height,
199                                                       QSSGRenderTextureFormat format)
200 {
201     Q_ASSERT(m_handle);
202     Q_ASSERT(m_maxMipLevel == 0);
203 
204     m_texTarget = QSSGRenderTextureTargetType::Texture2D_MS;
205 
206     qint32 maxWidth, maxHeight;
207     m_context->maxTextureSize(maxWidth, maxHeight);
208     if (width > maxWidth || height > maxHeight) {
209         qCCritical(RENDER_INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight);
210     }
211 
212     Q_ASSERT(format.isUncompressedTextureFormat()
213              || format.isDepthTextureFormat());
214 
215     m_backend->setMultisampledTextureData2D(m_handle, m_texTarget, sampleCount, format, width, height, true);
216 
217     m_width = width;
218     m_height = height;
219     m_sampleCount = sampleCount;
220     m_format = format;
221 }
222 
setTextureSubData(QSSGByteView newBuffer,quint8 inMipLevel,qint32 inXOffset,qint32 inYOffset,qint32 width,qint32 height,QSSGRenderTextureFormat format)223 void QSSGRenderTexture2D::setTextureSubData(QSSGByteView newBuffer,
224                                               quint8 inMipLevel,
225                                               qint32 inXOffset,
226                                               qint32 inYOffset,
227                                               qint32 width,
228                                               qint32 height,
229                                               QSSGRenderTextureFormat format)
230 {
231     Q_ASSERT(m_handle);
232     Q_ASSERT(inXOffset >= 0 && inYOffset >= 0 && width >= 0 && height >= 0);
233 
234     if (!format.isUncompressedTextureFormat()) {
235         qCCritical(RENDER_INVALID_PARAMETER, "Cannot set sub data for depth or compressed formats");
236         Q_ASSERT(false);
237         return;
238     }
239     qint32 subRectStride = width * format.getSizeofFormat();
240     if (qint32(newBuffer.size()) < subRectStride * height) {
241         qCCritical(RENDER_INVALID_PARAMETER, "Invalid sub rect buffer size");
242         Q_ASSERT(false);
243         return;
244     }
245     // nop
246     if (width == 0 || height == 0)
247         return;
248 
249     if (inXOffset + width > m_width || inYOffset + height > m_height) {
250         qCCritical(RENDER_INVALID_PARAMETER, "Sub rect outside existing image bounds");
251         Q_ASSERT(false);
252         return;
253     }
254 
255     // not handled yet
256     Q_ASSERT(!format.isDepthTextureFormat());
257 
258     m_backend->setTextureSubData2D(m_handle,
259                                    m_texTarget,
260                                    inMipLevel,
261                                    inXOffset,
262                                    inYOffset,
263                                    width,
264                                    height,
265                                    format,
266                                    newBuffer);
267 }
268 
generateMipmaps(QSSGRenderHint genType)269 void QSSGRenderTexture2D::generateMipmaps(QSSGRenderHint genType)
270 {
271     applyTexParams();
272     m_backend->generateMipMaps(m_handle, m_texTarget, genType);
273     qint32 maxDim = (m_width >= m_height) ? m_width : m_height;
274     m_maxMipLevel = qint32(float(std::log(maxDim)) / std::log(2.0f));
275     // we never create more level than m_maxLevel
276     m_maxMipLevel = qMin(m_maxMipLevel, m_maxLevel);
277 }
278 
bind()279 void QSSGRenderTexture2D::bind()
280 {
281     m_textureUnit = m_context->nextTextureUnit();
282 
283     m_backend->bindTexture(m_handle, m_texTarget, m_textureUnit);
284 
285     applyTexParams();
286     applyTexSwizzle();
287 }
288 
289 QT_END_NAMESPACE
290