Sunday, December 20, 2015


Doing anything significant whatsoever with QML, part 3: 30-bit color QQuickFramebufferObject support

My 30-bit QQuickFramebufferObject-derived Layer class (exposed to QML as SSLayer) renders 30-bit color correctly only when blitting directly to the framebuffer of the QQuickWindow that ultimately contains the SSLayer.  Using the SSLayer as a ShaderEffectSource, setting SSLayer's layer.enabled property to true, or otherwise causing SSLayer to be blitted into a texture node results in 8-bit-per-channel re-sampling.  Which is fucking unacceptable - SSLayer needs to both participate in the QML scene graph as a fully-fledged blendable, transformable, shade-able scene graph node and retain 30-bit fidelity when doing so.  Therefore, if this limitation can not be overcome in a reasonable way, I am going to forget that QML ever existed.

That being the case, let's get started.

Whoa!  Not so fast; this may not be completely hopeless.  The Qt docs insist that the QML scene graph is very flexible.  We'll see...

Firstly, QQuickFramebufferObject has a lot of moving parts, and some non-moving parts.  QQuickFramebufferObject::textureProvider(), for example, as far as I can tell, is never called.  Instead, QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) is responsible for instantiating QSGFramebufferObjectNode, which is a private class defined in qt5/qtdeclarative/src/quick/items/qquickframebufferobject.cpp.  So, let's start with making a QML Item (QQuickItem, in C++ land) that does nothing fancy save for rendering to a 30-bit texture when the Item's layer.enabled property is true.

In other words, the following, contained in an ApplicationWindow, should be free of 8-bit banding:

ThirtyBitImageItem {
    id: thirtyBitImageItem
    objectName: "thirtyBitImageItem"

    anchors.horizontalCenter: parent.horizontalCenter
    anchors.bottom: parent.bottom
    width: (implicitWidth / implicitHeight) * height
    anchors.margins: 10

    layer.enabled: true


ThirtyBitImageItem's C++ declaration:

class ThirtyBitImageItem
  : public QQuickItem
    Q_PROPERTY(Image* image READ image WRITE setImage)
    ThirtyBitImageItem(QQuickItem* parent=nullptr);

    Image* image();
    const Image* image() const;
    void setImage(Image* image);


public slots:

    Image* m_image;
    std::forward_list<QMetaObject::Connection> m_imageSignalConnections;

    QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData) override;
    void onImageSizeChanged(QSize size);


ThirtyBitImageItem is to be our 30-bit capable version of the built-in QML type, Image (implemented as QQuickImage in C++).  QQuickImage::updatePaintNode's implementation can be seen in qt5/qtdeclarative/src/quick/items/qquickimage.cpp:

QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)

    QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());

    // Copy over the current texture state into the texture provider...
    if (d->provider) {
        d->provider->m_smooth = d->smooth;
        d->provider->m_mipmap = d->mipmap;

    if (!texture || width() <= 0 || height() <= 0) {
        delete oldNode;
        return 0;

    QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
    if (!node) {
        d->pixmapChanged = true;
        node = d->sceneGraphContext()->createImageNode();

    // ...

    return node;


There's a lot going on, here.  QQuickImage::updatePaintNode is more complex than our first pass implementation needs to be, but it does provide some hints.  Perhaps all we need is our own version of QSGImageNode that supports 30-bit color?


Post a Comment

Subscribe to Post Comments [Atom]

<< Home


July 2009   August 2009   September 2009   October 2009   November 2009   December 2009   January 2010   September 2010   December 2010   January 2011   February 2011   April 2011   June 2011   August 2011   February 2012   June 2012   July 2012   August 2012   October 2012   November 2012   January 2014   April 2014   June 2014   August 2014   September 2014   October 2014   January 2015   March 2015   April 2015   June 2015   November 2015   December 2015   January 2016   June 2016   August 2016   January 2017   March 2017   April 2018  

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]