From: Gunnar Sletta Date: Fri, 15 Aug 2014 07:26:16 +0000 (+0200) Subject: Downscale textures which exceed the GL texture limit X-Git-Tag: v5.3.99+beta1~134 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8d0e307bc7f9f4458e6ea2d2fc0d6cd25be4fda9;p=platform%2Fupstream%2Fqtdeclarative.git Downscale textures which exceed the GL texture limit This way they will at least render. [ChangeLog][QtQuick] Images exceeding GL_MAX_TEXTURE_SIZE will be downscaled to fit so they will still show. Change-Id: I169ecac768036812b8e14265ec1a0a8902655666 Reviewed-by: Laszlo Agocs --- diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 6ebc479..4e76857 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -253,6 +253,7 @@ void QSGContext::renderContextInitialized(QSGRenderContext *renderContext) QSet exts = renderContext->openglContext()->extensions(); QByteArray all; foreach (const QByteArray &e, exts) all += ' ' + e; qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData(); + qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << renderContext->maxTextureSize(); } d->mutex.unlock(); @@ -392,6 +393,7 @@ QSGRenderContext::QSGRenderContext(QSGContext *context) , m_atlasManager(0) , m_depthStencilManager(0) , m_distanceFieldCacheManager(0) + , m_maxTextureSize(0) , m_brokenIBOs(false) , m_serializedRender(false) , m_attachToGLContext(true) @@ -493,6 +495,9 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) */ void QSGRenderContext::initialize(QOpenGLContext *context) { + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); + // Sanity check the surface format, in case it was overridden by the application QSurfaceFormat requested = m_sg->defaultSurfaceFormat(); QSurfaceFormat actual = context->format(); @@ -513,7 +518,6 @@ void QSGRenderContext::initialize(QOpenGLContext *context) m_sg->renderContextInitialized(this); #ifdef Q_OS_LINUX - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); const char *vendor = (const char *) funcs->glGetString(GL_VENDOR); if (strstr(vendor, "nouveau")) m_brokenIBOs = true; diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index b5271f9..7020226 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -126,6 +126,7 @@ public: static QSGRenderContext *from(QOpenGLContext *context); bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; } + int maxTextureSize() const { return m_maxTextureSize; } Q_SIGNALS: void initialized(); @@ -147,7 +148,7 @@ protected: QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager; QSet m_fontEnginesToClean; - + int m_maxTextureSize; bool m_brokenIBOs; bool m_serializedRender; bool m_attachToGLContext; diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 69edfab..abd9d48 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -671,12 +671,28 @@ void QSGPlainTexture::bind() bindTime = qsg_renderer_timer.nsecsElapsed(); // ### TODO: check for out-of-memory situations... - int w = m_image.width(); - int h = m_image.height(); QImage tmp = (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied) ? m_image : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + // Downscale the texture to fit inside the max texture limit if it is too big. + // It would be better if the image was already downscaled to the right size, + // but this information is not always available at that time, so as a last + // resort we can do it here. Texture coordinates are normalized, so it + // won't cause any problems and actual texture sizes will be written + // based on QSGTexture::textureSize which is updated after this, so that + // should be ok. + int max; + if (QSGRenderContext *rc = QSGRenderContext::from(context)) + max = rc->maxTextureSize(); + else + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); + if (tmp.width() > max || tmp.height() > max) { + tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_texture_size = tmp.size(); + } + if (tmp.width() * 4 != tmp.bytesPerLine()) tmp = tmp.copy(); @@ -726,7 +742,7 @@ void QSGPlainTexture::bind() if (profileFrames) swizzleTime = qsg_renderer_timer.nsecsElapsed(); - funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits()); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_texture_size.width(), m_texture_size.height(), 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits()); qint64 uploadTime = 0; if (profileFrames) @@ -741,7 +757,7 @@ void QSGPlainTexture::bind() if (profileFrames) { mipmapTime = qsg_renderer_timer.nsecsElapsed(); qCDebug(QSG_LOG_TIME_TEXTURE, - "plain texture uploaded in: %dms (%dx%d), bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d", + "plain texture uploaded in: %dms (%dx%d), bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d%s", int(mipmapTime / 1000000), m_texture_size.width(), m_texture_size.height(), int(bindTime / 1000000), @@ -750,7 +766,8 @@ void QSGPlainTexture::bind() (externalFormat == GL_BGRA ? "BGRA" : "RGBA"), (internalFormat == GL_BGRA ? "BGRA" : "RGBA"), int((uploadTime - swizzleTime)/1000000), - int((mipmapTime - uploadTime)/1000000)); + int((mipmapTime - uploadTime)/1000000), + m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : ""); } Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphTexturePrepare, ( @@ -760,7 +777,6 @@ void QSGPlainTexture::bind() uploadTime - swizzleTime, qsg_renderer_timer.nsecsElapsed() - uploadTime)); - m_texture_size = QSize(w, h); m_texture_rect = QRectF(0, 0, 1, 1); m_dirty_bind_options = false; diff --git a/tests/auto/quick/qquickimage/data/hugeImages.qml b/tests/auto/quick/qquickimage/data/hugeImages.qml new file mode 100644 index 0000000..f195299 --- /dev/null +++ b/tests/auto/quick/qquickimage/data/hugeImages.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 200 + height: 200 + + Image { + source: "toowide.png" + width: 100 + height: 100 + } + + Image { + x: 100 + source: "tootall.png" + width: 100 + height: 100 + } +} diff --git a/tests/auto/quick/qquickimage/data/tootall.png b/tests/auto/quick/qquickimage/data/tootall.png new file mode 100644 index 0000000..98d0f18 Binary files /dev/null and b/tests/auto/quick/qquickimage/data/tootall.png differ diff --git a/tests/auto/quick/qquickimage/data/toowide.png b/tests/auto/quick/qquickimage/data/toowide.png new file mode 100644 index 0000000..4a0a92c Binary files /dev/null and b/tests/auto/quick/qquickimage/data/toowide.png differ diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index a68a763..3b70c68 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include "../../shared/util.h" @@ -106,6 +107,7 @@ private slots: void sourceSizeChanges(); void correctStatus(); void highdpi(); + void hugeImages(); private: QQmlEngine engine; @@ -969,6 +971,21 @@ void tst_qquickimage::highdpi() delete obj; } +void tst_qquickimage::hugeImages() +{ + QQuickView view; + view.setSource(testFileUrl("hugeImages.qml")); + view.setGeometry(0, 0, 200, 200); + view.create(); + + QImage contents = view.grabWindow(); + + QCOMPARE(contents.pixel(0, 0), qRgba(255, 0, 0, 255)); + QCOMPARE(contents.pixel(99, 99), qRgba(255, 0, 0, 255)); + QCOMPARE(contents.pixel(100, 0), qRgba(0, 0, 255, 255)); + QCOMPARE(contents.pixel(199, 99), qRgba(0, 0, 255, 255)); +} + QTEST_MAIN(tst_qquickimage) #include "tst_qquickimage.moc"