// The address returned here will only be valid until next time this function is called.
// The program is return bound.
QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog)
{
    for (int i = 0; i < cachedPrograms.size(); ++i) {
        QGLEngineShaderProg *cachedProg = cachedPrograms[i];
        if (*cachedProg == prog) {
            // Move the program to the top of the list as a poor-man's cache algo
            cachedPrograms.move(i, 0);
            cachedProg->program->bind();
            return cachedProg;
        }
    }

    QScopedPointer<QGLEngineShaderProg> newProg;

    do {
        QByteArray fragSource;
        // Insert the custom stage before the srcPixel shader to work around an ATI driver bug
        // where you cannot forward declare a function that takes a sampler as argument.
        if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
            fragSource.append(prog.customStageSource);
        fragSource.append(qShaderSnippets[prog.mainFragShader]);
        fragSource.append(qShaderSnippets[prog.srcPixelFragShader]);
        if (prog.compositionFragShader)
            fragSource.append(qShaderSnippets[prog.compositionFragShader]);
        if (prog.maskFragShader)
            fragSource.append(qShaderSnippets[prog.maskFragShader]);

        QByteArray vertexSource;
        vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
        vertexSource.append(qShaderSnippets[prog.positionVertexShader]);

        QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram);

        CachedShader shaderCache(fragSource, vertexSource);
        bool inCache = shaderCache.load(shaderProgram.data(), QGLContext::currentContext());

        if (!inCache) {

            QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment));
            QByteArray description;
#if defined(QT_DEBUG)
            // Name the shader for easier debugging
            description.append("Fragment shader: main=");
            description.append(snippetNameStr(prog.mainFragShader));
            description.append(", srcPixel=");
            description.append(snippetNameStr(prog.srcPixelFragShader));
            if (prog.compositionFragShader) {
                description.append(", composition=");
                description.append(snippetNameStr(prog.compositionFragShader));
            }
            if (prog.maskFragShader) {
                description.append(", mask=");
                description.append(snippetNameStr(prog.maskFragShader));
            }
            fragShader->setObjectName(QString::fromLatin1(description));
#endif
            if (!fragShader->compileSourceCode(fragSource)) {
                qWarning() << "Warning:" << description << "failed to compile!";
                break;
            }

            QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex));
#if defined(QT_DEBUG)
            // Name the shader for easier debugging
            description.clear();
            description.append("Vertex shader: main=");
            description.append(snippetNameStr(prog.mainVertexShader));
            description.append(", position=");
            description.append(snippetNameStr(prog.positionVertexShader));
            vertexShader->setObjectName(QString::fromLatin1(description));
#endif
            if (!vertexShader->compileSourceCode(vertexSource)) {
                qWarning() << "Warning:" << description << "failed to compile!";
                break;
            }

            shaders.append(vertexShader.data());
            shaders.append(fragShader.data());
            shaderProgram->addShader(vertexShader.take());
            shaderProgram->addShader(fragShader.take());

            // We have to bind the vertex attribute names before the program is linked:
            shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
            if (prog.useTextureCoords)
                shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
            if (prog.useOpacityAttribute)
                shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
            if (prog.usePmvMatrixAttribute) {
                shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
                shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
                shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
            }
        }

        newProg.reset(new QGLEngineShaderProg(prog));
        newProg->program = shaderProgram.take();

        newProg->program->link();
        if (newProg->program->isLinked()) {
            if (!inCache)
                shaderCache.store(newProg->program, QGLContext::currentContext());
        } else {
            QLatin1String none("none");
            QLatin1String br("\n");
            QString error;
            error = QLatin1String("Shader program failed to link,");
#if defined(QT_DEBUG)
            error += QLatin1String("\n  Shaders Used:\n");
            for (int i = 0; i < newProg->program->shaders().count(); ++i) {
                QGLShader *shader = newProg->program->shaders().at(i);
                error += QLatin1String("    ") + shader->objectName() + QLatin1String(": \n")
                         + QLatin1String(shader->sourceCode()) + br;
            }
#endif
            error += QLatin1String("  Error Log:\n")
                     + QLatin1String("    ") + newProg->program->log();
            qWarning() << error;
            break;
        }

        newProg->program->bind();

        if (newProg->maskFragShader != QGLEngineSharedShaders::NoMaskFragmentShader) {
            GLuint location = newProg->program->uniformLocation("maskTexture");
            newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT);
        }

        if (cachedPrograms.count() > 30) {
            // The cache is full, so delete the last 5 programs in the list.
            // These programs will be least used, as a program us bumped to
            // the top of the list when it's used.
            for (int i = 0; i < 5; ++i) {
                delete cachedPrograms.last();
                cachedPrograms.removeLast();
            }
        }

        cachedPrograms.insert(0, newProg.data());
    } while (false);

    return newProg.take();
}
// The address returned here will only be valid until next time this function is called.
QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog)
{
    for (int i = 0; i < cachedPrograms.size(); ++i) {
        QGLEngineShaderProg *cachedProg = cachedPrograms[i];
        if (*cachedProg == prog) {
            // Move the program to the top of the list as a poor-man's cache algo
            cachedPrograms.move(i, 0);
            return cachedProg;
        }
    }

    QByteArray source;
    source.append(qShaderSnippets[prog.mainFragShader]);
    source.append(qShaderSnippets[prog.srcPixelFragShader]);
    if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
        source.append(prog.customStageSource);
    if (prog.compositionFragShader)
        source.append(qShaderSnippets[prog.compositionFragShader]);
    if (prog.maskFragShader)
        source.append(qShaderSnippets[prog.maskFragShader]);
    QGLShader* fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), this);
    fragShader->compileSourceCode(source);

    source.clear();
    source.append(qShaderSnippets[prog.mainVertexShader]);
    source.append(qShaderSnippets[prog.positionVertexShader]);
    QGLShader* vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), this);
    vertexShader->compileSourceCode(source);

#if defined(QT_DEBUG)
    // Name the shaders for easier debugging
    QByteArray description;
    description.append("Fragment shader: main=");
    description.append(snippetNameStr(prog.mainFragShader));
    description.append(", srcPixel=");
    description.append(snippetNameStr(prog.srcPixelFragShader));
    if (prog.compositionFragShader) {
        description.append(", composition=");
        description.append(snippetNameStr(prog.compositionFragShader));
    }
    if (prog.maskFragShader) {
        description.append(", mask=");
        description.append(snippetNameStr(prog.maskFragShader));
    }
    fragShader->setObjectName(QString::fromLatin1(description));

    description.clear();
    description.append("Vertex shader: main=");
    description.append(snippetNameStr(prog.mainVertexShader));
    description.append(", position=");
    description.append(snippetNameStr(prog.positionVertexShader));
    vertexShader->setObjectName(QString::fromLatin1(description));
#endif

    QGLEngineShaderProg* newProg = new QGLEngineShaderProg(prog);

    // If the shader program's not found in the cache, create it now.
    newProg->program = new QGLShaderProgram(ctxGuard.context(), this);
    newProg->program->addShader(vertexShader);
    newProg->program->addShader(fragShader);

    // We have to bind the vertex attribute names before the program is linked:
    newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
    if (newProg->useTextureCoords)
        newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
    if (newProg->useOpacityAttribute)
        newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);

    newProg->program->link();
    if (!newProg->program->isLinked()) {
        QLatin1String none("none");
        QLatin1String br("\n");
        QString error;
        error = QLatin1String("Shader program failed to link,")
#if defined(QT_DEBUG)
            + br
            + QLatin1String("  Shaders Used:") + br
            + QLatin1String("    ") + vertexShader->objectName() + QLatin1String(": ") + br
            + QLatin1String(vertexShader->sourceCode()) + br
            + QLatin1String("    ") + fragShader->objectName() + QLatin1String(": ") + br
            + QLatin1String(fragShader->sourceCode()) + br
#endif
            + QLatin1String("  Error Log:\n")
            + QLatin1String("    ") + newProg->program->log();
        qWarning() << error;
        delete newProg; // Deletes the QGLShaderProgram in it's destructor
        newProg = 0;
    }
    else {
        if (cachedPrograms.count() > 30) {
            // The cache is full, so delete the last 5 programs in the list.
            // These programs will be least used, as a program us bumped to
            // the top of the list when it's used.
            for (int i = 0; i < 5; ++i) {
                delete cachedPrograms.last();
                cachedPrograms.removeLast();
            }
        }

        cachedPrograms.insert(0, newProg);
    }

    return newProg;
}
Exemple #3
0
// The address returned here will only be valid until next time this function is called.
// The program is return bound.
QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog)
{
    for (int i = 0; i < cachedPrograms.size(); ++i) {
        QGLEngineShaderProg *cachedProg = cachedPrograms[i];
        if (*cachedProg == prog) {
            // Move the program to the top of the list as a poor-man's cache algo
            cachedPrograms.move(i, 0);
            cachedProg->program->bind();
            return cachedProg;
        }
    }

    QGLShader *vertexShader = 0;
    QGLShader *fragShader = 0;
    QGLEngineShaderProg *newProg = 0;
    bool success = false;

    do {
        QByteArray source;
        // Insert the custom stage before the srcPixel shader to work around an ATI driver bug
        // where you cannot forward declare a function that takes a sampler as argument.
        if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
            source.append(prog.customStageSource);
        source.append(qShaderSnippets[prog.mainFragShader]);
        source.append(qShaderSnippets[prog.srcPixelFragShader]);
        if (prog.compositionFragShader)
            source.append(qShaderSnippets[prog.compositionFragShader]);
        if (prog.maskFragShader)
            source.append(qShaderSnippets[prog.maskFragShader]);
        fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), this);
        QByteArray description;
#if defined(QT_DEBUG)
        // Name the shader for easier debugging
        description.append("Fragment shader: main=");
        description.append(snippetNameStr(prog.mainFragShader));
        description.append(", srcPixel=");
        description.append(snippetNameStr(prog.srcPixelFragShader));
        if (prog.compositionFragShader) {
            description.append(", composition=");
            description.append(snippetNameStr(prog.compositionFragShader));
        }
        if (prog.maskFragShader) {
            description.append(", mask=");
            description.append(snippetNameStr(prog.maskFragShader));
        }
        fragShader->setObjectName(QString::fromLatin1(description));
#endif
        if (!fragShader->compileSourceCode(source)) {
            qWarning() << "Warning:" << description << "failed to compile!";
            break;
        }

        source.clear();
        source.append(qShaderSnippets[prog.mainVertexShader]);
        source.append(qShaderSnippets[prog.positionVertexShader]);
        vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), this);
#if defined(QT_DEBUG)
        // Name the shader for easier debugging
        description.clear();
        description.append("Vertex shader: main=");
        description.append(snippetNameStr(prog.mainVertexShader));
        description.append(", position=");
        description.append(snippetNameStr(prog.positionVertexShader));
        vertexShader->setObjectName(QString::fromLatin1(description));
#endif
        if (!vertexShader->compileSourceCode(source)) {
            qWarning() << "Warning:" << description << "failed to compile!";
            break;
        }

        newProg = new QGLEngineShaderProg(prog);

        // If the shader program's not found in the cache, create it now.
        newProg->program = new QGLShaderProgram(ctxGuard.context(), this);
        newProg->program->addShader(vertexShader);
        newProg->program->addShader(fragShader);

        // We have to bind the vertex attribute names before the program is linked:
        newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
        if (newProg->useTextureCoords)
            newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
        if (newProg->useOpacityAttribute)
            newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
        if (newProg->usePmvMatrixAttribute) {
            newProg->program->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
            newProg->program->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
            newProg->program->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
        }

        newProg->program->link();
        if (!newProg->program->isLinked()) {
            QLatin1String none("none");
            QLatin1String br("\n");
            QString error;
            error = QLatin1String("Shader program failed to link,")
#if defined(QT_DEBUG)
                + br
                + QLatin1String("  Shaders Used:") + br
                + QLatin1String("    ") + vertexShader->objectName() + QLatin1String(": ") + br
                + QLatin1String(vertexShader->sourceCode()) + br
                + QLatin1String("    ") + fragShader->objectName() + QLatin1String(": ") + br
                + QLatin1String(fragShader->sourceCode()) + br
#endif
                + QLatin1String("  Error Log:\n")
                + QLatin1String("    ") + newProg->program->log();
            qWarning() << error;
            break;
        }

        newProg->program->bind();

        if (newProg->maskFragShader != QGLEngineSharedShaders::NoMaskFragmentShader) {
            GLuint location = newProg->program->uniformLocation("maskTexture");
            newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT);
        }

        if (cachedPrograms.count() > 30) {
            // The cache is full, so delete the last 5 programs in the list.
            // These programs will be least used, as a program us bumped to
            // the top of the list when it's used.
            for (int i = 0; i < 5; ++i) {
                delete cachedPrograms.last();
                cachedPrograms.removeLast();
            }
        }

        cachedPrograms.insert(0, newProg);

        success = true;
    } while (false);

    // Clean up everything if we weren't successful
    if (!success) {
        if (newProg) {
            delete newProg; // Also deletes the QGLShaderProgram which in turn deletes the QGLShaders
            newProg = 0;
        }
        else {
            if (vertexShader)
                delete vertexShader;
            if (fragShader)
                delete fragShader;
        }
    }

    return newProg;
}