An error occurred while loading the file. Please try again.
-
Eskil Abrahamsen Blomfeldt authored
This makes compilation possible of the saveTexture() function which is used to store the distance field glyph cache on disk when debugging. It also changes the warnings output from here to have no prefix, since this is anyway always available as the context in Qt 5. Change-Id: If3b64f0921a59c485b5321a5465369708e764afc Reviewed-by:
Yoann Lopes <yoann.lopes@theqtcompany.com>
911b940c
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtQuick module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** 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.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qsgadaptationlayer_p.h"
#include <qmath.h>
#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qrawfont_p.h>
#include <QtGui/qguiapplication.h>
#include <qdir.h>
#include <private/qquickprofiler_p.h>
#include <QElapsedTimer>
QT_BEGIN_NAMESPACE
static QElapsedTimer qsg_render_timer;
QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
: m_manager(man)
, m_pendingGlyphs(64)
{
Q_ASSERT(font.isValid());
QRawFontPrivate *fontD = QRawFontPrivate::get(font);
m_glyphCount = fontD->fontEngine->glyphCount();
m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
m_referenceFont = font;
m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution));
Q_ASSERT(m_referenceFont.isValid());
m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile);
}
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
{
}
QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
{
QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
if (data == m_glyphsData.end()) {
GlyphData gd;
gd.texture = &s_emptyTexture;
QPainterPath path = m_referenceFont.pathForGlyph(glyph);
gd.boundingRect = path.boundingRect();
data = m_glyphsData.insert(glyph, gd);
}
return data.value();
}
QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph, qreal pixelSize)
{
GlyphData &gd = glyphData(glyph);
qreal scale = fontScale(pixelSize);
Metrics m;
m.width = gd.boundingRect.width() * scale;
m.height = gd.boundingRect.height() * scale;
m.baselineX = gd.boundingRect.x() * scale;
m.baselineY = -gd.boundingRect.y() * scale;
return m;
}
void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
{
QSet<glyph_t> referencedGlyphs;
QSet<glyph_t> newGlyphs;
int count = glyphs.count();
for (int i = 0; i < count; ++i) {
glyph_t glyphIndex = glyphs.at(i);
if ((int) glyphIndex >= glyphCount()) {
qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
continue;
}
GlyphData &gd = glyphData(glyphIndex);
++gd.ref;
referencedGlyphs.insert(glyphIndex);
if (gd.texCoord.isValid() || m_populatingGlyphs.contains(glyphIndex))
continue;
m_populatingGlyphs.insert(glyphIndex);
if (gd.boundingRect.isEmpty()) {
gd.texCoord.width = 0;
gd.texCoord.height = 0;
} else {
newGlyphs.insert(glyphIndex);
}
}
referenceGlyphs(referencedGlyphs);
if (!newGlyphs.isEmpty())
requestGlyphs(newGlyphs);
}
void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
{
QSet<glyph_t> unusedGlyphs;
int count = glyphs.count();
for (int i = 0; i < count; ++i) {
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
glyph_t glyphIndex = glyphs.at(i);
GlyphData &gd = glyphData(glyphIndex);
if (--gd.ref == 0 && !gd.texCoord.isNull())
unusedGlyphs.insert(glyphIndex);
}
releaseGlyphs(unusedGlyphs);
}
void QSGDistanceFieldGlyphCache::update()
{
m_populatingGlyphs.clear();
if (m_pendingGlyphs.isEmpty())
return;
bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled() ||
QQuickProfiler::profilingSceneGraph();
if (profileFrames)
qsg_render_timer.start();
QList<QDistanceField> distanceFields;
for (int i = 0; i < m_pendingGlyphs.size(); ++i) {
distanceFields.append(QDistanceField(m_referenceFont,
m_pendingGlyphs.at(i),
m_doubleGlyphResolution));
}
qint64 renderTime = 0;
int count = m_pendingGlyphs.size();
if (profileFrames)
renderTime = qsg_render_timer.nsecsElapsed();
m_pendingGlyphs.reset();
storeGlyphs(distanceFields);
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
foreach (Texture texture, m_textures)
saveTexture(texture.textureId, texture.size.width(), texture.size.height());
#endif
if (QSG_LOG_TIME_GLYPH().isDebugEnabled()) {
quint64 now = qsg_render_timer.elapsed();
qCDebug(QSG_LOG_TIME_GLYPH,
"distancefield: %d glyphs prepared in %dms, rendering=%d, upload=%d",
count,
(int) now,
int(renderTime / 1000000),
int((now - (renderTime / 1000000))));
}
Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphAdaptationLayerFrame, (
count,
renderTime,
qsg_render_timer.nsecsElapsed() - renderTime));
}
void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
{
QVector<quint32> invalidatedGlyphs;
int count = glyphs.count();
for (int i = 0; i < count; ++i) {
GlyphPosition glyph = glyphs.at(i);
GlyphData &gd = glyphData(glyph.glyph);
if (!gd.texCoord.isNull())
invalidatedGlyphs.append(glyph.glyph);
gd.texCoord.xMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
gd.texCoord.yMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
gd.texCoord.x = glyph.position.x();
gd.texCoord.y = glyph.position.y();
gd.texCoord.width = gd.boundingRect.width();
gd.texCoord.height = gd.boundingRect.height();
}
if (!invalidatedGlyphs.isEmpty()) {
QLinkedList<QSGDistanceFieldGlyphConsumer *>::iterator it = m_registeredNodes.begin();
while (it != m_registeredNodes.end()) {
(*it)->invalidateGlyphs(invalidatedGlyphs);
++it;
}
}
}
void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
{
Q_UNUSED(ownerElement);
}
void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
{
Q_UNUSED(ownerElement);
}
void QSGDistanceFieldGlyphCache::processPendingGlyphs()
{
/* Intentionally empty */
}
void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
{
int i = m_textures.indexOf(tex);
if (i == -1) {
m_textures.append(tex);
i = m_textures.size() - 1;
} else {
m_textures[i].size = tex.size;
}
Texture *texture = &(m_textures[i]);
QVector<quint32> invalidatedGlyphs;
int count = glyphs.count();
for (int j = 0; j < count; ++j) {
glyph_t glyphIndex = glyphs.at(j);
GlyphData &gd = glyphData(glyphIndex);
if (gd.texture != &s_emptyTexture)
invalidatedGlyphs.append(glyphIndex);
gd.texture = texture;
}
if (!invalidatedGlyphs.isEmpty()) {
QLinkedList<QSGDistanceFieldGlyphConsumer*>::iterator it = m_registeredNodes.begin();
while (it != m_registeredNodes.end()) {
(*it)->invalidateGlyphs(invalidatedGlyphs);
++it;
}
}
}
void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
{
int count = glyphs.count();
for (int i = 0; i < count; ++i)
m_pendingGlyphs.add(glyphs.at(i));
}
void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize)
{
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
int count = m_textures.count();
for (int i = 0; i < count; ++i) {
Texture &tex = m_textures[i];
if (tex.textureId == oldTex) {
tex.textureId = newTex;
tex.size = newTexSize;
return;
}
}
}
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
#include <QtGui/qopenglfunctions.h>
void QSGDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height) const
{
QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
GLuint fboId;
functions->glGenFramebuffers(1, &fboId);
GLuint tmpTexture = 0;
functions->glGenTextures(1, &tmpTexture);
functions->glBindTexture(GL_TEXTURE_2D, tmpTexture);
functions->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
functions->glBindTexture(GL_TEXTURE_2D, 0);
functions->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
functions->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
tmpTexture, 0);
functions->glActiveTexture(GL_TEXTURE0);
functions->glBindTexture(GL_TEXTURE_2D, textureId);
functions->glDisable(GL_STENCIL_TEST);
functions->glDisable(GL_DEPTH_TEST);
functions->glDisable(GL_SCISSOR_TEST);
functions->glDisable(GL_BLEND);
GLfloat textureCoordinateArray[8];
textureCoordinateArray[0] = 0.0f;
textureCoordinateArray[1] = 0.0f;
textureCoordinateArray[2] = 1.0f;
textureCoordinateArray[3] = 0.0f;
textureCoordinateArray[4] = 1.0f;
textureCoordinateArray[5] = 1.0f;
textureCoordinateArray[6] = 0.0f;
textureCoordinateArray[7] = 1.0f;
GLfloat vertexCoordinateArray[8];
vertexCoordinateArray[0] = -1.0f;
vertexCoordinateArray[1] = -1.0f;
vertexCoordinateArray[2] = 1.0f;
vertexCoordinateArray[3] = -1.0f;
vertexCoordinateArray[4] = 1.0f;
vertexCoordinateArray[5] = 1.0f;
vertexCoordinateArray[6] = -1.0f;
vertexCoordinateArray[7] = 1.0f;
functions->glViewport(0, 0, width, height);
functions->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
functions->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
{
static const char *vertexShaderSource =
"attribute vec4 vertexCoordsArray; \n"
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
"attribute vec2 textureCoordArray; \n"
"varying vec2 textureCoords; \n"
"void main(void) \n"
"{ \n"
" gl_Position = vertexCoordsArray; \n"
" textureCoords = textureCoordArray; \n"
"} \n";
static const char *fragmentShaderSource =
"varying vec2 textureCoords; \n"
"uniform sampler2D texture; \n"
"void main() \n"
"{ \n"
" gl_FragColor = texture2D(texture, textureCoords); \n"
"} \n";
GLuint vertexShader = functions->glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = functions->glCreateShader(GL_FRAGMENT_SHADER);
if (vertexShader == 0 || fragmentShader == 0) {
GLenum error = functions->glGetError();
qWarning("QSGDistanceFieldGlyphCache::saveTexture: Failed to create shaders. (GL error: %x)",
error);
return;
}
functions->glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
functions->glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
functions->glCompileShader(vertexShader);
GLint len = 1;
functions->glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
char infoLog[2048];
functions->glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
if (qstrlen(infoLog) > 0)
qWarning("Problems compiling vertex shader:\n %s", infoLog);
functions->glCompileShader(fragmentShader);
functions->glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
if (qstrlen(infoLog) > 0)
qWarning("Problems compiling fragment shader:\n %s", infoLog);
GLuint shaderProgram = functions->glCreateProgram();
functions->glAttachShader(shaderProgram, vertexShader);
functions->glAttachShader(shaderProgram, fragmentShader);
functions->glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
functions->glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
functions->glLinkProgram(shaderProgram);
functions->glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
if (qstrlen(infoLog) > 0)
qWarning("Problems linking shaders:\n %s", infoLog);
functions->glUseProgram(shaderProgram);
functions->glEnableVertexAttribArray(0);
functions->glEnableVertexAttribArray(1);
int textureUniformLocation = functions->glGetUniformLocation(shaderProgram, "texture");
functions->glUniform1i(textureUniformLocation, 0);
}
functions->glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
{
GLenum error = functions->glGetError();
if (error != GL_NO_ERROR)
qWarning("glDrawArrays reported error 0x%x", error);
}
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
uchar *data = new uchar[width * height * 4];
functions->glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
QImage image(data, width, height, QImage::Format_ARGB32);
QByteArray fileName = m_referenceFont.familyName().toLatin1() + '_' + QByteArray::number(textureId);
fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
image.save(QString::fromLocal8Bit(fileName));
{
GLenum error = functions->glGetError();
if (error != GL_NO_ERROR)
qWarning("glReadPixels reported error 0x%x", error);
}
functions->glDisableVertexAttribArray(0);
functions->glDisableVertexAttribArray(1);
functions->glDeleteFramebuffers(1, &fboId);
functions->glDeleteTextures(1, &tmpTexture);
delete[] data;
}
#endif
void QSGNodeVisitorEx::visitChildren(QSGNode *node)
{
for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
switch (child->type()) {
case QSGNode::ClipNodeType: {
QSGClipNode *c = static_cast<QSGClipNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
break;
}
case QSGNode::TransformNodeType: {
QSGTransformNode *c = static_cast<QSGTransformNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
break;
}
case QSGNode::OpacityNodeType: {
QSGOpacityNode *c = static_cast<QSGOpacityNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
break;
}
case QSGNode::GeometryNodeType: {
if (child->flags() & QSGNode::IsVisitableNode) {
QSGVisitableNode *v = static_cast<QSGVisitableNode*>(child);
v->accept(this);
} else {
QSGGeometryNode *c = static_cast<QSGGeometryNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
}
break;
}
case QSGNode::RootNodeType: {
QSGRootNode *root = static_cast<QSGRootNode*>(child);
if (visit(root))
visitChildren(root);
endVisit(root);
491492493494495496497498499500501502503504505
break;
}
case QSGNode::BasicNodeType: {
visitChildren(child);
break;
}
default:
Q_UNREACHABLE();
break;
}
}
}
QT_END_NAMESPACE