blob: dd938df0f3ccbd933bc52a6dba405b9a683c07d4 [file] [log] [blame]
/****************************************************************************
**
** 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(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(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(INVALID_OPERATION, "Width or height is greater than max texture size (%d, %d)", maxWidth, maxHeight);
}
if (inLevels < 1) {
qCCritical(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(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(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(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(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