| /**************************************************************************** | 
 | ** | 
 | ** Copyright (C) 2008-2012 NVIDIA Corporation. | 
 | ** Copyright (C) 2019 The Qt Company Ltd. | 
 | ** Contact: https://www.qt.io/licensing/ | 
 | ** | 
 | ** This file is part of Qt Quick 3D. | 
 | ** | 
 | ** $QT_BEGIN_LICENSE:GPL$ | 
 | ** Commercial License Usage | 
 | ** Licensees holding valid commercial Qt licenses may use this file in | 
 | ** accordance with the commercial license agreement provided with the | 
 | ** Software or, alternatively, in accordance with the terms contained in | 
 | ** a written agreement between you and The Qt Company. For licensing terms | 
 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
 | ** information use the contact form at https://www.qt.io/contact-us. | 
 | ** | 
 | ** GNU General Public License Usage | 
 | ** Alternatively, this file may be used under the terms of the GNU | 
 | ** General Public License version 3 or (at your option) any later version | 
 | ** approved by the KDE Free Qt Foundation. The licenses are as published by | 
 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 | 
 | ** included in the packaging of this file. Please review the following | 
 | ** information to ensure the GNU General Public License requirements will | 
 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
 | ** | 
 | ** $QT_END_LICENSE$ | 
 | ** | 
 | ****************************************************************************/ | 
 |  | 
 | #include <QtQuick3DRender/private/qssgrendercontext_p.h> | 
 | #include <QtQuick3DUtils/private/qssgutils_p.h> | 
 | #include <QtQuick/QSGTexture> | 
 |  | 
 | QT_BEGIN_NAMESPACE | 
 |  | 
 | QSSGRenderTexture2D::QSSGRenderTexture2D(const QSSGRef<QSSGRenderContext> &context) | 
 |     : QSSGRenderTextureBase(context, QSSGRenderTextureTargetType::Texture2D), m_width(0), m_height(0) | 
 | { | 
 | } | 
 |  | 
 | QSSGRenderTexture2D::QSSGRenderTexture2D(const QSSGRef<QSSGRenderContext> &context, QSGTexture *qsgTexture) | 
 |     : QSSGRenderTextureBase(context, QSSGRenderTextureTargetType::Texture2D, false), | 
 |       m_width(qsgTexture->textureSize().width()), | 
 |       m_height(qsgTexture->textureSize().height()) | 
 | { | 
 |     Q_ASSERT(!m_ownsTexture); | 
 |     Q_ASSERT(!m_handle); | 
 |     m_handle = reinterpret_cast<QSSGRenderBackend::QSSGRenderBackendTextureObject>(quintptr(qsgTexture->textureId())); | 
 |     m_texTarget = QSSGRenderTextureTargetType::Texture2D; | 
 |  | 
 |     m_format = qsgTexture->hasAlphaChannel() ? QSSGRenderTextureFormat::RGBA8 | 
 |                                              : QSSGRenderTextureFormat::RGB8; | 
 |     m_sampleCount = 1; // TODO | 
 | } | 
 |  | 
 | QSSGRenderTexture2D::~QSSGRenderTexture2D() | 
 | { | 
 | } | 
 |  | 
 | QSSGTextureDetails QSSGRenderTexture2D::textureDetails() const | 
 | { | 
 |     return QSSGTextureDetails(m_width, m_height, 0, m_sampleCount, m_format); | 
 | } | 
 |  | 
 | void QSSGRenderTexture2D::setTextureData(QSSGByteView newBuffer, | 
 |                                            quint8 inMipLevel, | 
 |                                            qint32 width, | 
 |                                            qint32 height, | 
 |                                            QSSGRenderTextureFormat format, | 
 |                                            QSSGRenderTextureFormat formatDest) | 
 | { | 
 |     Q_ASSERT(m_handle); | 
 |  | 
 |     // check if we should compress this texture | 
 |  | 
 |     if (inMipLevel == 0) { | 
 |         m_width = width; | 
 |         m_height = height; | 
 |         m_format = format; | 
 |  | 
 |         // We re-use textures and this might have been a MSAA texture before | 
 |         // for resue we must completely destroy the texture object and create a new one | 
 |         // The same is true for immutable textures | 
 |         if (m_texTarget == QSSGRenderTextureTargetType::Texture2D_MS || m_immutable) { | 
 |             m_backend->releaseTexture(m_handle); | 
 |             m_texTarget = QSSGRenderTextureTargetType::Texture2D; | 
 |             m_sampleCount = 1; | 
 |             m_handle = m_backend->createTexture(); | 
 |         } | 
 |  | 
 |         if (formatDest.isCompressedTextureFormat()) { | 
 |             bool compress = format.isUncompressedTextureFormat(); | 
 |             bool appropriateSizes = !((width % 4) || (height % 4)); | 
 |  | 
 |             // we only compress multiple of 4 textures | 
 |             if (compress && !appropriateSizes) | 
 |                 compress = false; | 
 |  | 
 |             if (compress) { | 
 |                 // This seems like a very dubious line here.  If we are compressing then the | 
 |                 // image | 
 |                 // is really 1/4 the width and height? - CN | 
 |                 m_width = width / 4; | 
 |                 m_height = height / 4; | 
 |                 m_format = formatDest; | 
 |             } | 
 |         } else if (formatDest.isUncompressedTextureFormat()) { | 
 |             m_format = formatDest; | 
 |         } | 
 |     } | 
 |  | 
 |     if (m_maxMipLevel < inMipLevel) { | 
 |         m_maxMipLevel = inMipLevel; | 
 |     } | 
 |  | 
 |     // get max size and check value | 
 |     qint32 maxWidth, maxHeight; | 
 |     m_context->maxTextureSize(maxWidth, maxHeight); | 
 |     if (width > maxWidth || height > maxHeight) { | 
 |         qCCritical(RENDER_INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight); | 
 |     } | 
 |     if (format.isUncompressedTextureFormat() || format.isDepthTextureFormat()) { | 
 |         m_backend->setTextureData2D(m_handle, | 
 |                                     m_texTarget, | 
 |                                     inMipLevel, | 
 |                                     m_format, | 
 |                                     width, | 
 |                                     height, | 
 |                                     0, | 
 |                                     format, | 
 |                                     newBuffer); | 
 |     } else if (format.isCompressedTextureFormat()) { | 
 |         m_backend->setCompressedTextureData2D(m_handle, | 
 |                                               m_texTarget, | 
 |                                               inMipLevel, | 
 |                                               format, | 
 |                                               width, | 
 |                                               height, | 
 |                                               0, | 
 |                                               newBuffer); | 
 |     } | 
 |     // Set our texture parameters to a default that will look the best | 
 |     if (inMipLevel > 0) | 
 |         setMinFilter(QSSGRenderTextureMinifyingOp::LinearMipmapLinear); | 
 | } | 
 |  | 
 | void QSSGRenderTexture2D::setTextureStorage(qint32 inLevels, | 
 |                                               qint32 width, | 
 |                                               qint32 height, | 
 |                                               QSSGRenderTextureFormat formaInternal, | 
 |                                               QSSGRenderTextureFormat format, | 
 |                                               QSSGByteView dataBuffer) | 
 | { | 
 |     Q_ASSERT(m_handle); | 
 |  | 
 |     if (!m_context->supportsShaderImageLoadStore()) { | 
 |         qCCritical(RENDER_INVALID_OPERATION, "The extension Shader_Image_Load_Store is not supported"); | 
 |         return; | 
 |     } | 
 |  | 
 |     m_width = width; | 
 |     m_height = height; | 
 |     m_format = formaInternal; | 
 |     if (format == QSSGRenderTextureFormat::Unknown) | 
 |         format = formaInternal; | 
 |  | 
 |     // get max size and check value | 
 |     qint32 maxWidth, maxHeight; | 
 |     m_context->maxTextureSize(maxWidth, maxHeight); | 
 |     if (width > maxWidth || height > maxHeight) { | 
 |         qCCritical(RENDER_INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight); | 
 |     } | 
 |  | 
 |     if (inLevels < 1) { | 
 |         qCCritical(RENDER_INVALID_PARAMETER, "inLevels is less than 1 (%d)", inLevels); | 
 |     } | 
 |  | 
 |     m_maxMipLevel = inLevels - 1; // we count from 0 | 
 |  | 
 |     // only uncompressed formats are supported and no depth | 
 |     if (formaInternal.isUncompressedTextureFormat()) { | 
 |         m_backend->createTextureStorage2D(m_handle, m_texTarget, inLevels, formaInternal, width, height); | 
 |  | 
 |         m_immutable = true; | 
 |         m_texTarget = QSSGRenderTextureTargetType::Texture2D; | 
 |  | 
 |         if (dataBuffer.size() > 0) | 
 |             m_backend->setTextureSubData2D(m_handle, m_texTarget, 0, 0, 0, width, height, format, dataBuffer); | 
 |  | 
 |         if (inLevels > 1) | 
 |             setMinFilter(QSSGRenderTextureMinifyingOp::LinearMipmapLinear); | 
 |     } | 
 | } | 
 |  | 
 | void QSSGRenderTexture2D::setTextureDataMultisample(qint32 sampleCount, | 
 |                                                       qint32 width, | 
 |                                                       qint32 height, | 
 |                                                       QSSGRenderTextureFormat format) | 
 | { | 
 |     Q_ASSERT(m_handle); | 
 |     Q_ASSERT(m_maxMipLevel == 0); | 
 |  | 
 |     m_texTarget = QSSGRenderTextureTargetType::Texture2D_MS; | 
 |  | 
 |     qint32 maxWidth, maxHeight; | 
 |     m_context->maxTextureSize(maxWidth, maxHeight); | 
 |     if (width > maxWidth || height > maxHeight) { | 
 |         qCCritical(RENDER_INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight); | 
 |     } | 
 |  | 
 |     Q_ASSERT(format.isUncompressedTextureFormat() | 
 |              || format.isDepthTextureFormat()); | 
 |  | 
 |     m_backend->setMultisampledTextureData2D(m_handle, m_texTarget, sampleCount, format, width, height, true); | 
 |  | 
 |     m_width = width; | 
 |     m_height = height; | 
 |     m_sampleCount = sampleCount; | 
 |     m_format = format; | 
 | } | 
 |  | 
 | void QSSGRenderTexture2D::setTextureSubData(QSSGByteView newBuffer, | 
 |                                               quint8 inMipLevel, | 
 |                                               qint32 inXOffset, | 
 |                                               qint32 inYOffset, | 
 |                                               qint32 width, | 
 |                                               qint32 height, | 
 |                                               QSSGRenderTextureFormat format) | 
 | { | 
 |     Q_ASSERT(m_handle); | 
 |     Q_ASSERT(inXOffset >= 0 && inYOffset >= 0 && width >= 0 && height >= 0); | 
 |  | 
 |     if (!format.isUncompressedTextureFormat()) { | 
 |         qCCritical(RENDER_INVALID_PARAMETER, "Cannot set sub data for depth or compressed formats"); | 
 |         Q_ASSERT(false); | 
 |         return; | 
 |     } | 
 |     qint32 subRectStride = width * format.getSizeofFormat(); | 
 |     if (qint32(newBuffer.size()) < subRectStride * height) { | 
 |         qCCritical(RENDER_INVALID_PARAMETER, "Invalid sub rect buffer size"); | 
 |         Q_ASSERT(false); | 
 |         return; | 
 |     } | 
 |     // nop | 
 |     if (width == 0 || height == 0) | 
 |         return; | 
 |  | 
 |     if (inXOffset + width > m_width || inYOffset + height > m_height) { | 
 |         qCCritical(RENDER_INVALID_PARAMETER, "Sub rect outside existing image bounds"); | 
 |         Q_ASSERT(false); | 
 |         return; | 
 |     } | 
 |  | 
 |     // not handled yet | 
 |     Q_ASSERT(!format.isDepthTextureFormat()); | 
 |  | 
 |     m_backend->setTextureSubData2D(m_handle, | 
 |                                    m_texTarget, | 
 |                                    inMipLevel, | 
 |                                    inXOffset, | 
 |                                    inYOffset, | 
 |                                    width, | 
 |                                    height, | 
 |                                    format, | 
 |                                    newBuffer); | 
 | } | 
 |  | 
 | void QSSGRenderTexture2D::generateMipmaps(QSSGRenderHint genType) | 
 | { | 
 |     applyTexParams(); | 
 |     m_backend->generateMipMaps(m_handle, m_texTarget, genType); | 
 |     qint32 maxDim = (m_width >= m_height) ? m_width : m_height; | 
 |     m_maxMipLevel = qint32(float(std::log(maxDim)) / std::log(2.0f)); | 
 |     // we never create more level than m_maxLevel | 
 |     m_maxMipLevel = qMin(m_maxMipLevel, m_maxLevel); | 
 | } | 
 |  | 
 | void QSSGRenderTexture2D::bind() | 
 | { | 
 |     m_textureUnit = m_context->nextTextureUnit(); | 
 |  | 
 |     m_backend->bindTexture(m_handle, m_texTarget, m_textureUnit); | 
 |  | 
 |     applyTexParams(); | 
 |     applyTexSwizzle(); | 
 | } | 
 |  | 
 | QT_END_NAMESPACE |