从Image获取纹理并在QQuickFramebufferObject中绘制它。并非所有实例都呈现

时间:2017-08-24 14:26:13

标签: qt opengl qml qtquick2

我在做什么,简而言之

  • MyItem
  • 派生QQuickFramebufferObject班级
  • MyItem我有一个QQuickItem* sourceItem属性,我可以从中获取纹理并将其绘制成三角形
  • 从QML我提供了一个名为sourceItem的图像到MyItem。此图片有cache: false
  • 当图片完成加载时,我等待一帧,然后在update
  • 上致电MyItem
  • 我有一个6x6网格的MyItem个实例

问题是MyItem的某些实例没有得出任何结论:

screenshot

有什么想法吗?

我的代码:

的main.cpp

#include <QQmlApplicationEngine>
#include <QGuiApplication>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
#include <QSGTextureProvider>
#include <QSGTexture>
#include <QQuickWindow>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include "propertyhelper.h" // this file is from http://syncor.blogspot.bg/2014/11/qt-auto-property.html

class MyItem : public QQuickFramebufferObject {
    Q_OBJECT

    AUTO_PROPERTY(QQuickItem*, sourceItem)

public:
    Renderer *createRenderer() const;
};

class MyItemRenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions {
public:
    MyItemRenderer() {
        initializeOpenGLFunctions();

        m_program.addShaderFromSourceCode(QOpenGLShader::Vertex,
            "in highp vec2 aPos;\
            out highp vec2 vTexCoord;\
            \
            void main() {\
                gl_Position = vec4(aPos, 0.0, 1.0);\
                vTexCoord = aPos * .5  + .5;\
            }"
        );
        m_program.addShaderFromSourceCode(QOpenGLShader::Fragment,
            "in highp vec2 vTexCoord;\
            out vec4 outputColor;\
            uniform sampler2D uTex;\
            \
            void main() {\
                outputColor = texture(uTex, vTexCoord);\
            }"
        );
        m_program.link();
        m_program.setUniformValue("uTex", 0);

        createGeometry();
    }

    void synchronize(QQuickFramebufferObject* qqfbo){
        auto item = (MyItem*)qqfbo;

        m_window = item->window();
        m_tex = item->sourceItem()->textureProvider()->texture();
    }

    QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
        QOpenGLFramebufferObjectFormat format;
        format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
        return new QOpenGLFramebufferObject(size, format);
    }

    void paintGeometry() {
        m_program.enableAttributeArray("aPos");
        m_program.setAttributeArray("aPos", m_vertices.constData());
        glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
        m_program.disableAttributeArray("aPos");
    }

    void createGeometry() {
        m_vertices << QVector2D(-1, -1);
        m_vertices << QVector2D(1, -1);
        m_vertices << QVector2D(-1, 1);
    }

    void render() {
        glDisable(GL_DEPTH_TEST);
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);

        m_program.bind();

        glActiveTexture(GL_TEXTURE0);
        m_tex->bind();
        paintGeometry();

        m_window->resetOpenGLState();
    }

private:
    QQuickWindow* m_window;
    QVector<QVector2D> m_vertices;
    QSGTexture* m_tex;
    QOpenGLShaderProgram m_program;
};

QQuickFramebufferObject::Renderer *MyItem::createRenderer() const {
    return new MyItemRenderer();
}

int main(int argc, char** argv) {
    qmlRegisterType<MyItem>("MyItem", 1, 0, "MyItem");
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

#include "main.moc"

main.qml

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    visible: true
    id: window
    width: 600
    height: 600

    Flow {
        anchors.fill: parent
        Repeater {
            model: 36
            delegate: MyItemWrapper {
                width: 100
                height: 100
            }
        }
    }
}

MyItemWrapper.qml

import QtQuick 2.5
import MyItem 1.0

Item {
    Image {
        x: -100000 // hide the sourceItem
        id: sourceItem
        layer.enabled: true
        source: "https://images-na.ssl-images-amazon.com/images/M/MV5BMTg2MTMyMzU0M15BMl5BanBnXkFtZTgwOTU3ODk4NTE@._V1_SX300.jpg"
        cache: false

        function updateCppItemOnce() {
            window.afterRendering.disconnect(updateCppItemOnce);
            cppItem.update();
        }

        onStatusChanged: {
            if (status == Image.Ready) {
                window.afterRendering.connect(updateCppItemOnce);
            }
        }
    }

    MyItem {
        sourceItem: sourceItem
        anchors.fill: parent
        id: cppItem
    }
}

备注:

  • 我尝试启用OpenGL日志记录但没有消息
  • 我在其他3台PC上试过测试用例,所有这些都比这个更弱/更慢。它们不会发生错误。所以我认为这个bug只发生在快速PC上 - 这可能是一个时间问题。

2 个答案:

答案 0 :(得分:1)

我现在意识到,我只是连接到update()信号,而不是我调用item->sourceItem()->textureProvider()->textureChanged()的笨重方式。这样做,现在它就像一个魅力。

我仍然想知道为什么我原来的方式会失败,但是:)

答案 1 :(得分:0)

Gunnar Sletta在my bugreport中提供了解决方案作为评论。

  

我怀疑问题是你使用了'afterRendering&#39;信号。当在线程渲染循环上运行时,此信号将在渲染完成时触发,并且将在主线程上几乎立即调用插槽。根据线程渲染循环在图像完成加载时的位置,这意味着您可能会立即调用插槽。当场景图到达同步阶段时,处理节点的顺序是任意的,因此可以在图像之前处理一些节点,之后处理一些节点。在图像获得纹理之前处理的那些被获取为空并且在获得有效纹理之后处理的那些。

     

连接到afterSynchronization信号会更好,因为在图像的同步保证完成之后,这实际上会先触发。

确实,使用afterSynchronizing解决了我的问题。