diff options
author | Eirik Aavitsland <eirik.aavitsland@qt.io> | 2020-05-14 13:25:04 +0200 |
---|---|---|
committer | Eirik Aavitsland <eirik.aavitsland@qt.io> | 2020-05-26 13:30:13 +0200 |
commit | 8afbdbdc9f33623987e9ecc42d2c7b05754e31c9 (patch) | |
tree | 261c023b1d6aec30e8c6273fa6ca506f1ef7bcf1 | |
parent | 7accd9cc74d88a17b08c83ea4b8275557dc318bb (diff) |
RHI implementation of compressed texture atlasing
Automatic atlasing (which is enabled by default for normal textures)
was added as an experimental, opt-in feature in 5.11. This commit
redoes that implementation for RHI, and enables it by default.
[ChangeLog] Enable automatic atlasing of compressed textures (can be
disabled with QSG_DISABLE_COMPRESSED_ATLAS=1)
Fixes: QTBUG-78582
Change-Id: Ia8344fffdc8dd8fb476bf6a77057c359e4816487
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
16 files changed, 135 insertions, 96 deletions
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp index ed37305374..5afc681fa0 100644 --- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp @@ -43,13 +43,10 @@ #include <QtCore/QElapsedTimer> #include <QtCore/QtMath> -#include <QOpenGLContext> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> #include <QtGui/QSurface> #include <QtGui/QWindow> -#include <QOpenGLFunctions> -#include <QOpenGLTexture> #include <QDebug> #include <private/qqmlglobal_p.h> @@ -59,14 +56,11 @@ QT_BEGIN_NAMESPACE -static QElapsedTimer qsg_renderer_timer; - namespace QSGCompressedAtlasTexture { -Atlas::Atlas(const QSize &size, uint format) - : QSGOpenGLAtlasTexture::AtlasBase(size) - , m_format(format) +Atlas::Atlas(QSGDefaultRenderContext *rc, const QSize &size, uint format) + : QSGRhiAtlasTexture::AtlasBase(rc, size), m_format(format) { } @@ -74,8 +68,10 @@ Atlas::~Atlas() { } -Texture *Atlas::create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size, const QSize &paddedSize) +Texture *Atlas::create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size) { + // Align reservation to 16x16, >= any compressed block size + QSize paddedSize(((size.width() + 15) / 16) * 16, ((size.height() + 15) / 16) * 16); // No need to lock, as manager already locked it. QRect rect = m_allocator.allocate(paddedSize); if (rect.width() > 0 && rect.height() > 0) { @@ -86,53 +82,53 @@ Texture *Atlas::create(const QByteArray &data, int dataLength, int dataOffset, c return nullptr; } -void Atlas::generateTexture() +bool Atlas::generateTexture() { - int bytesPerBlock = 8; - switch (m_format) { - case QOpenGLTexture::RGBA8_ETC2_EAC: - case QOpenGLTexture::RGBA_DXT3: - case QOpenGLTexture::RGBA_DXT5: - bytesPerBlock = 16; - default: - break; + QSGCompressedTexture::FormatInfo fmt = QSGCompressedTexture::formatInfo(m_format); + QRhiTexture::Flags flags(QRhiTexture::UsedAsTransferSource | QRhiTexture::UsedAsCompressedAtlas); + flags.setFlag(QRhiTexture::sRGB, fmt.isSRGB); + m_texture = m_rhi->newTexture(fmt.rhiFormat, m_size, 1, flags); + if (!m_texture) + return false; + + if (!m_texture->build()) { + delete m_texture; + m_texture = nullptr; + return false; } - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_format, - m_size.width(), m_size.height(), 0, - (m_size.width() / 4 * m_size.height() / 4) * bytesPerBlock, - nullptr); + qCDebug(QSG_LOG_TEXTUREIO, "Created compressed atlas of size %dx%d for format 0x%x (rhi: %d)", + m_size.width(), m_size.height(), m_format, fmt.rhiFormat); + + return true; } -void Atlas::uploadPendingTexture(int i) +void Atlas::enqueueTextureUpload(QSGRhiAtlasTexture::TextureBase *t, QRhiResourceUpdateBatch *rcub) { - Texture *texture = static_cast<Texture*>(m_pending_uploads.at(i)); + Texture *texture = static_cast<Texture *>(t); const QRect &r = texture->atlasSubRect(); - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - funcs->glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, - r.x(), r.y(), r.width(), r.height(), m_format, - texture->sizeInBytes(), - texture->data().constData() + texture->dataOffset()); + const char *rawData = texture->data().constData() + texture->dataOffset(); + QRhiTextureSubresourceUploadDescription subresDesc(rawData, texture->sizeInBytes()); + subresDesc.setSourceSize(texture->textureSize()); + subresDesc.setDestinationTopLeft(r.topLeft()); - qCDebug(QSG_LOG_TIME_TEXTURE).nospace() << "compressed atlastexture uploaded in: " << qsg_renderer_timer.elapsed() - << "ms (" << texture->textureSize().width() << "x" - << texture->textureSize().height() << ")"; + QRhiTextureUploadDescription desc(QRhiTextureUploadEntry(0, 0, subresDesc)); + rcub->uploadTexture(m_texture, desc); - // TODO: consider releasing the data (as is done in the regular atlas)? - // The advantage of keeping this data around is that it makes it much easier - // to remove the texture from the atlas + qCDebug(QSG_LOG_TEXTUREIO, "compressed atlastexture upload, size %dx%d format 0x%x", + t->textureSize().width(), t->textureSize().height(), m_format); } -Texture::Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size) - : QSGOpenGLAtlasTexture::TextureBase(atlas, textureRect) - , m_nonatlas_texture(nullptr) - , m_data(data) - , m_size(size) - , m_dataLength(dataLength) - , m_dataOffset(dataOffset) +Texture::Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, + int dataOffset, const QSize &size) + : QSGRhiAtlasTexture::TextureBase(atlas, textureRect), + m_nonatlas_texture(nullptr), + m_data(data), + m_size(size), + m_dataLength(dataLength), + m_dataOffset(dataOffset) { float w = atlas->size().width(); float h = atlas->size().height(); diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h index a2d6d96dfe..fd60aa5f02 100644 --- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h @@ -53,11 +53,9 @@ #include <QtCore/QSize> -#include <qopengl.h> - #include <QtQuick/QSGTexture> #include <QtQuick/private/qsgareaallocator_p.h> -#include <QtQuick/private/qsgopenglatlastexture_p.h> +#include <QtQuick/private/qsgrhiatlastexture_p.h> QT_BEGIN_NAMESPACE @@ -67,16 +65,17 @@ namespace QSGCompressedAtlasTexture { class Texture; -class Atlas : public QSGOpenGLAtlasTexture::AtlasBase +class Atlas : public QSGRhiAtlasTexture::AtlasBase { public: - Atlas(const QSize &size, uint format); + Atlas(QSGDefaultRenderContext *rc, const QSize &size, uint format); ~Atlas(); - void generateTexture() override; - void uploadPendingTexture(int i) override; + bool generateTexture() override; + void enqueueTextureUpload(QSGRhiAtlasTexture::TextureBase *t, + QRhiResourceUpdateBatch *rcub) override; - Texture *create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size, const QSize &paddedSize); + Texture *create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size); uint format() const { return m_format; } @@ -84,7 +83,7 @@ private: uint m_format; }; -class Texture : public QSGOpenGLAtlasTexture::TextureBase +class Texture : public QSGRhiAtlasTexture::TextureBase { Q_OBJECT public: diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp index 59cc967167..9e3cc83433 100644 --- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp @@ -163,23 +163,23 @@ void QSGCompressedTexture::bind() #endif // QT_CONFIG(opengl) } -static QPair<QRhiTexture::Format, bool> toRhiCompressedFormat(uint glinternalformat) +QSGCompressedTexture::FormatInfo QSGCompressedTexture::formatInfo(quint32 glTextureFormat) { - switch (glinternalformat) { + switch (glTextureFormat) { case QOpenGLTexture::RGB_DXT1: return { QRhiTexture::BC1, false }; case QOpenGLTexture::SRGB_DXT1: return { QRhiTexture::BC1, true }; case QOpenGLTexture::RGBA_DXT3: - return { QRhiTexture::BC3, false }; + return { QRhiTexture::BC2, false }; case QOpenGLTexture::SRGB_Alpha_DXT3: - return { QRhiTexture::BC3, true }; + return { QRhiTexture::BC2, true }; case QOpenGLTexture::RGBA_DXT5: - return { QRhiTexture::BC5, false }; + return { QRhiTexture::BC3, false }; case QOpenGLTexture::SRGB_Alpha_DXT5: - return { QRhiTexture::BC5, true }; + return { QRhiTexture::BC3, true }; case QOpenGLTexture::RGB8_ETC2: return { QRhiTexture::ETC2_RGB8, false }; @@ -288,23 +288,23 @@ void QSGCompressedTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdate return; } - const QPair<QRhiTexture::Format, bool> fmt = toRhiCompressedFormat(m_textureData.glInternalFormat()); - if (fmt.first == QRhiTexture::UnknownFormat) { + FormatInfo fmt = formatInfo(m_textureData.glInternalFormat()); + if (fmt.rhiFormat == QRhiTexture::UnknownFormat) { qWarning("Unknown compressed format 0x%x", m_textureData.glInternalFormat()); return; } QRhiTexture::Flags texFlags; - if (fmt.second) + if (fmt.isSRGB) texFlags |= QRhiTexture::sRGB; - if (!rhi->isTextureFormatSupported(fmt.first, texFlags)) { + if (!rhi->isTextureFormatSupported(fmt.rhiFormat, texFlags)) { qWarning("Unsupported compressed format 0x%x", m_textureData.glInternalFormat()); return; } if (!m_texture) { - m_texture = rhi->newTexture(fmt.first, m_size, 1, texFlags); + m_texture = rhi->newTexture(fmt.rhiFormat, m_size, 1, texFlags); if (!m_texture->build()) { qWarning("Failed to create QRhiTexture for compressed data"); delete m_texture; diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h index d584f0e2d4..00e37098b1 100644 --- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h @@ -54,11 +54,14 @@ #include <private/qtexturefiledata_p.h> #include <private/qsgcontext_p.h> #include <private/qsgtexture_p.h> +#include <private/qrhi_p.h> #include <QQuickTextureFactory> #include <QOpenGLFunctions> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO); + class Q_QUICK_PRIVATE_EXPORT QSGCompressedTexture : public QSGTexture { Q_OBJECT @@ -78,6 +81,12 @@ public: QTextureFileData textureData() const; + struct FormatInfo + { + QRhiTexture::Format rhiFormat; + bool isSRGB; + }; + static FormatInfo formatInfo(quint32 glTextureFormat); static bool formatIsOpaque(quint32 glTextureFormat); protected: @@ -101,11 +110,10 @@ public: int textureByteCount() const override; QSize textureSize() const override; + const QTextureFileData *textureData() const { return &m_textureData; } + protected: QTextureFileData m_textureData; - -private: - friend class QSGOpenGLAtlasTexture::Manager; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index a5d352b7f1..d51d9e7bbe 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -347,8 +347,8 @@ QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompre { // This is only used for atlasing compressed textures. Returning null implies no atlas. - if (m_rhi) { - // ### + if (m_rhi && QThread::currentThread() == m_rhi->thread()) { + return m_rhiAtlasManager->create(factory); } else if (openglContext() && QThread::currentThread() == openglContext()->thread()) { // The atlas implementation is only supported from the render thread return m_glAtlasManager->create(factory); diff --git a/src/quick/scenegraph/util/qsgopenglatlastexture.cpp b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp index aa5ee3d7b8..94f77c8a08 100644 --- a/src/quick/scenegraph/util/qsgopenglatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp @@ -71,7 +71,7 @@ int qt_sg_envInt(const char *name, int defaultValue); static QElapsedTimer qsg_renderer_timer; -DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS) +// DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS) namespace QSGOpenGLAtlasTexture { @@ -141,6 +141,11 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory) { + Q_UNUSED(factory) + return nullptr; + + // DirectGL path disabled +#if 0 QSGTexture *t = nullptr; if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid()) return t; @@ -176,6 +181,7 @@ QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory) t = i.value()->create(data, factory->m_textureData.dataLength(), factory->m_textureData.dataOffset(), size, paddedSize); } return t; +#endif } AtlasBase::AtlasBase(const QSize &size) diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp index 172cfc4791..2935c61e7e 100644 --- a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp @@ -49,6 +49,8 @@ #include <private/qquickprofiler_p.h> #include <private/qsgdefaultrendercontext_p.h> #include <private/qsgtexture_p.h> +#include <private/qsgcompressedtexture_p.h> +#include <private/qsgcompressedatlastexture_p.h> #include <qtquick_tracepoints_p.h> @@ -63,7 +65,7 @@ int qt_sg_envInt(const char *name, int defaultValue); static QElapsedTimer qsg_renderer_timer; -//DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS) +DEFINE_BOOL_CONFIG_OPTION(qsgDisableCompressedAtlas, QSG_DISABLE_COMPRESSED_ATLAS) namespace QSGRhiAtlasTexture { @@ -105,7 +107,6 @@ void Manager::invalidate() m_atlas = nullptr; } - #if 0 QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.begin(); while (i != m_atlases.end()) { i.value()->invalidate(); @@ -113,7 +114,6 @@ void Manager::invalidate() ++i; } m_atlases.clear(); -#endif } QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) @@ -131,38 +131,27 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory) { - Q_UNUSED(factory); - return nullptr; - // ### - -#if 0 QSGTexture *t = nullptr; - if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid()) + if (qsgDisableCompressedAtlas() || !factory->textureData()->isValid()) return t; - // TODO: further abstract the atlas and remove this restriction - unsigned int format = factory->m_textureData.glInternalFormat(); - switch (format) { - case QOpenGLTexture::RGB8_ETC1: - case QOpenGLTexture::RGB8_ETC2: - case QOpenGLTexture::RGBA8_ETC2_EAC: - case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: - break; - default: + unsigned int format = factory->textureData()->glInternalFormat(); + QSGCompressedTexture::FormatInfo fmt = QSGCompressedTexture::formatInfo(format); + if (!m_rhi->isTextureFormatSupported(fmt.rhiFormat)) return t; - } - QSize size = factory->m_textureData.size(); + QSize size = factory->textureData()->size(); if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) { QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format); - if (i == m_atlases.end()) - i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format)); - // must be multiple of 4 - QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4); - QByteArray data = factory->m_textureData.data(); - t = i.value()->create(data, factory->m_textureData.dataLength(), factory->m_textureData.dataOffset(), size, paddedSize); + if (i == m_atlases.cend()) { + auto newAtlas = new QSGCompressedAtlasTexture::Atlas(m_rc, m_atlas_size, format); + i = m_atlases.insert(format, newAtlas); + } + const QTextureFileData *cmpData = factory->textureData(); + t = i.value()->create(cmpData->data(), cmpData->dataLength(), cmpData->dataOffset(), size); } -#endif + + return t; } AtlasBase::AtlasBase(QSGDefaultRenderContext *rc, const QSize &size) diff --git a/tests/manual/scenegraph_lancelot/data/images/compressed_tex.qml b/tests/manual/scenegraph_lancelot/data/images/compressed_tex.qml new file mode 100644 index 0000000000..3fdb54948a --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/images/compressed_tex.qml @@ -0,0 +1,41 @@ +import QtQuick 2.14 + +Rectangle { + height: 480 + width: 320 + color: "green" + + Grid { + anchors.fill: parent + columns: 2 + topPadding: 40 + padding: 64 + spacing: 64 + rowSpacing: 48 + + Image { + source: "../shared/o1_bc1.ktx" + } + Image { + source: "../shared/o1.png" + } + Image { + source: "../shared/o2_bc1.ktx" + } + Image { + source: "../shared/o2.png" + } + Image { + source: "../shared/t1_bc2.ktx" + } + Image { + source: "../shared/t1.png" + } + Image { + source: "../shared/t2_bc2.ktx" + } + Image { + source: "../shared/t2.png" + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shared/o1.png b/tests/manual/scenegraph_lancelot/data/shared/o1.png Binary files differnew file mode 100644 index 0000000000..a122b5ac33 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/o1.png diff --git a/tests/manual/scenegraph_lancelot/data/shared/o1_bc1.ktx b/tests/manual/scenegraph_lancelot/data/shared/o1_bc1.ktx Binary files differnew file mode 100644 index 0000000000..d61194a745 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/o1_bc1.ktx diff --git a/tests/manual/scenegraph_lancelot/data/shared/o2.png b/tests/manual/scenegraph_lancelot/data/shared/o2.png Binary files differnew file mode 100644 index 0000000000..fe88b7d03c --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/o2.png diff --git a/tests/manual/scenegraph_lancelot/data/shared/o2_bc1.ktx b/tests/manual/scenegraph_lancelot/data/shared/o2_bc1.ktx Binary files differnew file mode 100644 index 0000000000..780bb1f3fe --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/o2_bc1.ktx diff --git a/tests/manual/scenegraph_lancelot/data/shared/t1.png b/tests/manual/scenegraph_lancelot/data/shared/t1.png Binary files differnew file mode 100644 index 0000000000..35e2168c0f --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/t1.png diff --git a/tests/manual/scenegraph_lancelot/data/shared/t1_bc2.ktx b/tests/manual/scenegraph_lancelot/data/shared/t1_bc2.ktx Binary files differnew file mode 100644 index 0000000000..bc109940e5 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/t1_bc2.ktx diff --git a/tests/manual/scenegraph_lancelot/data/shared/t2.png b/tests/manual/scenegraph_lancelot/data/shared/t2.png Binary files differnew file mode 100644 index 0000000000..59fca9eb58 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/t2.png diff --git a/tests/manual/scenegraph_lancelot/data/shared/t2_bc2.ktx b/tests/manual/scenegraph_lancelot/data/shared/t2_bc2.ktx Binary files differnew file mode 100644 index 0000000000..fc9ac0841b --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shared/t2_bc2.ktx |