Make sure we rebuild batches properly after a Blending material change.
authorGunnar Sletta <gunnar.sletta@jollamobile.com>
Fri, 25 Apr 2014 14:21:00 +0000 (16:21 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 1 May 2014 12:28:05 +0000 (14:28 +0200)
If a node sent DirtyGeometry and DirtyMaterial in the same round and
DirtyGeometry triggered its batch to be invalidated, we would take the
no-batch code path which did set the rebuild state to BuildBatches. This
looks ok on screen, but if the node changed from alpha to opaque, it
would not be put into an opaque batch. For things like full screen
rectangles, this can potentially hurt performance.

To prevent doing a full rebuild on any material change on batchless
items (aka all Rectangle nodes), we store the blend state of the material
in the element and do a full rebuild only when blend state changes.

Change-Id: Ifdf06fb72ef02ca47a135c821ddbcbe0d164ca29
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h

index 791e06e..254f32a 100644 (file)
@@ -606,11 +606,6 @@ void Element::computeBounds()
 
 BatchCompatibility Batch::isMaterialCompatible(Element *e) const
 {
-    // If material has changed between opaque and translucent, it is not compatible
-    QSGMaterial *m = e->node->activeMaterial();
-    if (isOpaque != ((m->flags() & QSGMaterial::Blending) == 0))
-        return BatchBreaksOnBlending;
-
     Element *n = first;
     // Skip to the first node other than e which has not been removed
     while (n && (n == e || n->removed))
@@ -621,6 +616,7 @@ BatchCompatibility Batch::isMaterialCompatible(Element *e) const
     if (!n)
         return BatchIsCompatible;
 
+    QSGMaterial *m = e->node->activeMaterial();
     QSGMaterial *nm = n->node->activeMaterial();
     return (nm->type() == m->type() && nm->compare(m) == 0)
             ? BatchIsCompatible
@@ -1185,11 +1181,12 @@ void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
     if (state & QSGNode::DirtyMaterial && node->type() == QSGNode::GeometryNodeType) {
         Element *e = shadowNode->element();
         if (e) {
-            if (e->batch) {
-                BatchCompatibility compat = e->batch->isMaterialCompatible(e);
-                if (compat == BatchBreaksOnBlending)
-                    m_rebuild |= Renderer::FullRebuild;
-                else if (compat == BatchBreaksOnCompare)
+            bool blended = hasMaterialWithBlending(static_cast<QSGGeometryNode *>(node));
+            if (e->isMaterialBlended != blended) {
+                m_rebuild |= Renderer::FullRebuild;
+                e->isMaterialBlended = blended;
+            } else if (e->batch) {
+                if (e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare)
                     invalidateBatchAndOverlappingRenderOrders(e->batch);
             } else {
                 m_rebuild |= Renderer::BuildBatches;
index 3972a98..96b99a2 100644 (file)
@@ -67,6 +67,12 @@ class Updater;
 class Renderer;
 class ShaderManager;
 
+inline bool hasMaterialWithBlending(QSGGeometryNode *n)
+{
+    return (n->opaqueMaterial() ? n->opaqueMaterial()->flags() & QSGMaterial::Blending
+                                : n->material()->flags() & QSGMaterial::Blending);
+}
+
 struct Pt {
     float x, y;
 
@@ -163,6 +169,7 @@ struct Element {
         , removed(false)
         , orphaned(false)
         , isRenderNode(false)
+        , isMaterialBlended(n ? hasMaterialWithBlending(n) : false)
     {
     }
 
@@ -187,6 +194,7 @@ struct Element {
     uint removed : 1;
     uint orphaned : 1;
     uint isRenderNode : 1;
+    uint isMaterialBlended : 1;
 };
 
 struct RenderNodeElement : public Element {
@@ -233,7 +241,6 @@ struct DrawSet
 
 enum BatchCompatibility
 {
-    BatchBreaksOnBlending,
     BatchBreaksOnCompare,
     BatchIsCompatible
 };