Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Better than QML Canvas? How to draw multiple items without sucking up CPU
Forum Updated to NodeBB v4.3 + New Features

Better than QML Canvas? How to draw multiple items without sucking up CPU

Scheduled Pinned Locked Moved Solved QML and Qt Quick
5 Posts 2 Posters 1.2k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    mxyn
    wrote on last edited by
    #1

    I am drawing multiple overlapping polygons and labels on a video. The polygons and labels are all saved in a QVariantList, and I draw based upon that list. I currently draw all polygons and labels using a QML Canvas element. If there is a change to any polygon/label, I clear the canvas and redraw everything.
    I wanted to make it use even less CPU so I used ONE Canvas element per polygon. That way, if a polygon changed colors, I would only need to redraw that changed polygon without redrawing the other polygons. My theory was wrong. This solution uses even more CPU!

    In my research, it looks like I might want to use QML Scene Graph. What do you guys think?
    Here is my current qml code.

    import QtQuick 2.0
    import DrawZones 1.0
    
    
    Item {
        property real lineWidth: 3 
        anchors.fill: parent
    
        Zones {
            id: zoneOB 
            anchors.fill: parent
            objectName: "zone"
    
            property string zoneColor: "black"
    
            onZoneListChanged: {
                /*canvas.requestPaint();*/
    	    canvas0.requestPaint();
    	    canvas1.requestPaint();
    	    canvas2.requestPaint();
    	    canvas3.requestPaint();
    	    canvas4.requestPaint();
    	    canvas5.requestPaint();
                var zonelist = getZoneList;
                /*  debug only
                console.log("zonelist size: ", zonelist.length, "; zone size: ", zonelist[0].length);
                for (var i in zonelist) {
                    for (var j in zonelist[i]) {
                        if (j < zonelist[0].length-1)
                            console.log("P", i, "j=", j, ":", zonelist[i][j].x, ",", zonelist[i][j].y)
                        else
                            console.log("Color", i, "j=", j, ":", zonelist[i][j])
                    }
                }*/
            }
        } //Zone
    
        FontLoader{id: localFont; source: "Cabin-SemiBold.ttf" }
    
        Image {        
    	objectName: "test"
            visible: false
            id: tea
            anchors.top: parent.top
            width: 60
            height: 60
            source: "pics/RadarCarLarge1.png"
        }
    
        function drawZone(cntxt, zn, color, w) {
            for (var zcol in zn){
                if (zcol == 0) {
                    cntxt.beginPath();
                    cntxt.lineWidth = lineWidth+w;
                    cntxt.moveTo(zn[zcol].x, zn[zcol].y);
                }
                else if (zcol < zn.length-2) {
                    cntxt.lineTo(zn[zcol].x, zn[zcol].y);                
                }
                else {
                    cntxt.strokeStyle = color; 
                    cntxt.closePath();
                    cntxt.stroke();
                }            
            }
        }
    
        function clearCanvas(cntx){
            cntx.beginPath();
            cntx.clearRect(0, 0, width, height);
            cntx.fill();
        }
    
        Canvas {
            id: canvas 
            anchors.fill: parent
            objectName:"canvas"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
     
            onPaint: {
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                // Draw zones
    	    for (var zrow in zlist) {
                    if ((zrow !=0 ) && (zrow !=1))
                    {
                        drawZone(context, zlist[zrow], zlist[zrow][5], 5); //zlist[zrow][5] );
                        drawZone(context, zlist[zrow], zlist[zrow][4], 0); //zlist[zrow][5] );  
                    }
                }         
            } //onPaint()
        } /* Canvas */
    
    
        Canvas {
            id: canvas0 
            anchors.fill: parent
            objectName:"canvas0"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
            property real idx: 0 
            onPaint: {
                console.log("canvas 0");
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                if(zlist.length > idx)
                {
    		if(zlist[0][4]!= "OFF" && zlist[0][5]!="OFF")
                    {
                	    // Draw zone
                        drawZone(context, zlist[0], zlist[0][5], 5);
                        drawZone(context, zlist[0], zlist[0][4], 0);
                    }
                }
            } //onPaint()
        } /* Canvas */
    
        Canvas {
            id: canvas1 
            anchors.fill: parent
            objectName:"canvas1"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
            property real idx: 1 
            onPaint: {
    console.log("canvas 1");
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                if(zlist.length > idx)
                {
    		if(zlist[1][4]!= "OFF" && zlist[1][5]!="OFF")
                    {
                	    // Draw zone
                        drawZone(context, zlist[1], zlist[1][5], 5);
                        drawZone(context, zlist[1], zlist[1][4], 0);
                    }
                }
            } //onPaint()
        } /* Canvas */
    
        Canvas {
            id: canvas2 
            anchors.fill: parent
            objectName:"canvas2"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
            property real idx: 2 
            onPaint: {
    console.log("canvas 2");
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                if(zlist.length > idx)
                {
    		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                    {
                	    // Draw zone
                        drawZone(context, zlist[idx], zlist[idx][5], 5);
                        drawZone(context, zlist[idx], zlist[idx][4], 0);
                    }
                }
            } //onPaint()
        } /* Canvas */
    
        Canvas {
            id: canvas3 
            anchors.fill: parent
            objectName:"canvas3"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
            property real idx: 3 
            onPaint: {
    console.log("canvas 3");
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                if(zlist.length > idx)
                {
    		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                    {
                	    // Draw zone
                        drawZone(context, zlist[idx], zlist[idx][5], 5);
                        drawZone(context, zlist[idx], zlist[idx][4], 0);
                    }
                }
            } //onPaint()
        } /* Canvas */
    
        Canvas {
            id: canvas4 
            anchors.fill: parent
            objectName:"canvas4"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
            property real idx: 4 
            onPaint: {
    console.log("canvas 4");
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                if(zlist.length > idx)
                {
    		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                    {
                	    // Draw zone
                        drawZone(context, zlist[idx], zlist[idx][5], 5);
                        drawZone(context, zlist[idx], zlist[idx][4], 0);
                    }
                }
            } //onPaint()
        } /* Canvas */
    
        Canvas {
            id: canvas5 
            anchors.fill: parent
            objectName:"canvas5"
            renderTarget: Canvas.FramebufferObject
            renderStrategy: Canvas.Cooperative
            state: "black"
            property real idx: 5 
            onPaint: {
    console.log("canvas 5");
                // Get drawing context
                var context = getContext("2d");
                clearCanvas(context);  // Make canvas all white
    
                var zlist = zoneOB.getZoneList;            
                if(zlist.length > idx)
                {
    		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                    {
                	    // Draw zone
                        drawZone(context, zlist[idx], zlist[idx][5], 5);
                        drawZone(context, zlist[idx], zlist[idx][4], 0);
                    }
                }
            } //onPaint()
        } /* Canvas */
    
    
    
    
    
    }
    
    
    J.HilkJ 1 Reply Last reply
    0
    • M mxyn

      I am drawing multiple overlapping polygons and labels on a video. The polygons and labels are all saved in a QVariantList, and I draw based upon that list. I currently draw all polygons and labels using a QML Canvas element. If there is a change to any polygon/label, I clear the canvas and redraw everything.
      I wanted to make it use even less CPU so I used ONE Canvas element per polygon. That way, if a polygon changed colors, I would only need to redraw that changed polygon without redrawing the other polygons. My theory was wrong. This solution uses even more CPU!

      In my research, it looks like I might want to use QML Scene Graph. What do you guys think?
      Here is my current qml code.

      import QtQuick 2.0
      import DrawZones 1.0
      
      
      Item {
          property real lineWidth: 3 
          anchors.fill: parent
      
          Zones {
              id: zoneOB 
              anchors.fill: parent
              objectName: "zone"
      
              property string zoneColor: "black"
      
              onZoneListChanged: {
                  /*canvas.requestPaint();*/
      	    canvas0.requestPaint();
      	    canvas1.requestPaint();
      	    canvas2.requestPaint();
      	    canvas3.requestPaint();
      	    canvas4.requestPaint();
      	    canvas5.requestPaint();
                  var zonelist = getZoneList;
                  /*  debug only
                  console.log("zonelist size: ", zonelist.length, "; zone size: ", zonelist[0].length);
                  for (var i in zonelist) {
                      for (var j in zonelist[i]) {
                          if (j < zonelist[0].length-1)
                              console.log("P", i, "j=", j, ":", zonelist[i][j].x, ",", zonelist[i][j].y)
                          else
                              console.log("Color", i, "j=", j, ":", zonelist[i][j])
                      }
                  }*/
              }
          } //Zone
      
          FontLoader{id: localFont; source: "Cabin-SemiBold.ttf" }
      
          Image {        
      	objectName: "test"
              visible: false
              id: tea
              anchors.top: parent.top
              width: 60
              height: 60
              source: "pics/RadarCarLarge1.png"
          }
      
          function drawZone(cntxt, zn, color, w) {
              for (var zcol in zn){
                  if (zcol == 0) {
                      cntxt.beginPath();
                      cntxt.lineWidth = lineWidth+w;
                      cntxt.moveTo(zn[zcol].x, zn[zcol].y);
                  }
                  else if (zcol < zn.length-2) {
                      cntxt.lineTo(zn[zcol].x, zn[zcol].y);                
                  }
                  else {
                      cntxt.strokeStyle = color; 
                      cntxt.closePath();
                      cntxt.stroke();
                  }            
              }
          }
      
          function clearCanvas(cntx){
              cntx.beginPath();
              cntx.clearRect(0, 0, width, height);
              cntx.fill();
          }
      
          Canvas {
              id: canvas 
              anchors.fill: parent
              objectName:"canvas"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
       
              onPaint: {
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  // Draw zones
      	    for (var zrow in zlist) {
                      if ((zrow !=0 ) && (zrow !=1))
                      {
                          drawZone(context, zlist[zrow], zlist[zrow][5], 5); //zlist[zrow][5] );
                          drawZone(context, zlist[zrow], zlist[zrow][4], 0); //zlist[zrow][5] );  
                      }
                  }         
              } //onPaint()
          } /* Canvas */
      
      
          Canvas {
              id: canvas0 
              anchors.fill: parent
              objectName:"canvas0"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
              property real idx: 0 
              onPaint: {
                  console.log("canvas 0");
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  if(zlist.length > idx)
                  {
      		if(zlist[0][4]!= "OFF" && zlist[0][5]!="OFF")
                      {
                  	    // Draw zone
                          drawZone(context, zlist[0], zlist[0][5], 5);
                          drawZone(context, zlist[0], zlist[0][4], 0);
                      }
                  }
              } //onPaint()
          } /* Canvas */
      
          Canvas {
              id: canvas1 
              anchors.fill: parent
              objectName:"canvas1"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
              property real idx: 1 
              onPaint: {
      console.log("canvas 1");
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  if(zlist.length > idx)
                  {
      		if(zlist[1][4]!= "OFF" && zlist[1][5]!="OFF")
                      {
                  	    // Draw zone
                          drawZone(context, zlist[1], zlist[1][5], 5);
                          drawZone(context, zlist[1], zlist[1][4], 0);
                      }
                  }
              } //onPaint()
          } /* Canvas */
      
          Canvas {
              id: canvas2 
              anchors.fill: parent
              objectName:"canvas2"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
              property real idx: 2 
              onPaint: {
      console.log("canvas 2");
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  if(zlist.length > idx)
                  {
      		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                      {
                  	    // Draw zone
                          drawZone(context, zlist[idx], zlist[idx][5], 5);
                          drawZone(context, zlist[idx], zlist[idx][4], 0);
                      }
                  }
              } //onPaint()
          } /* Canvas */
      
          Canvas {
              id: canvas3 
              anchors.fill: parent
              objectName:"canvas3"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
              property real idx: 3 
              onPaint: {
      console.log("canvas 3");
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  if(zlist.length > idx)
                  {
      		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                      {
                  	    // Draw zone
                          drawZone(context, zlist[idx], zlist[idx][5], 5);
                          drawZone(context, zlist[idx], zlist[idx][4], 0);
                      }
                  }
              } //onPaint()
          } /* Canvas */
      
          Canvas {
              id: canvas4 
              anchors.fill: parent
              objectName:"canvas4"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
              property real idx: 4 
              onPaint: {
      console.log("canvas 4");
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  if(zlist.length > idx)
                  {
      		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                      {
                  	    // Draw zone
                          drawZone(context, zlist[idx], zlist[idx][5], 5);
                          drawZone(context, zlist[idx], zlist[idx][4], 0);
                      }
                  }
              } //onPaint()
          } /* Canvas */
      
          Canvas {
              id: canvas5 
              anchors.fill: parent
              objectName:"canvas5"
              renderTarget: Canvas.FramebufferObject
              renderStrategy: Canvas.Cooperative
              state: "black"
              property real idx: 5 
              onPaint: {
      console.log("canvas 5");
                  // Get drawing context
                  var context = getContext("2d");
                  clearCanvas(context);  // Make canvas all white
      
                  var zlist = zoneOB.getZoneList;            
                  if(zlist.length > idx)
                  {
      		if(zlist[idx][4]!= "OFF" && zlist[idx][5]!="OFF")
                      {
                  	    // Draw zone
                          drawZone(context, zlist[idx], zlist[idx][5], 5);
                          drawZone(context, zlist[idx], zlist[idx][4], 0);
                      }
                  }
              } //onPaint()
          } /* Canvas */
      
      
      
      
      
      }
      
      
      J.HilkJ Offline
      J.HilkJ Offline
      J.Hilk
      Moderators
      wrote on last edited by J.Hilk
      #2

      hi @mxyn
      The Canvas element is probably the slowest way to draw stuff in QML. Quick to set up and nice for small details, but that's about it.

      The fastest, and probably most useful one for you is:
      Subclassing QQuickItem and using https://doc.qt.io/qt-5/qquickitem.html#updatePaintNode to draw directly onto the scene graph using OpenGL


      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      M 1 Reply Last reply
      2
      • J.HilkJ J.Hilk

        hi @mxyn
        The Canvas element is probably the slowest way to draw stuff in QML. Quick to set up and nice for small details, but that's about it.

        The fastest, and probably most useful one for you is:
        Subclassing QQuickItem and using https://doc.qt.io/qt-5/qquickitem.html#updatePaintNode to draw directly onto the scene graph using OpenGL

        M Offline
        M Offline
        mxyn
        wrote on last edited by
        #3

        @J-Hilk Please correct me if I'm wrong, but updatePaintNode would not allow me to only remove/modify one item from the scene without having to repaint the entire scene? So if I wanted to have multiple, independently drawn items, each item would have to be it's own QQuickItem?

        -Mercy

        J.HilkJ 1 Reply Last reply
        0
        • M mxyn

          @J-Hilk Please correct me if I'm wrong, but updatePaintNode would not allow me to only remove/modify one item from the scene without having to repaint the entire scene? So if I wanted to have multiple, independently drawn items, each item would have to be it's own QQuickItem?

          -Mercy

          J.HilkJ Offline
          J.HilkJ Offline
          J.Hilk
          Moderators
          wrote on last edited by J.Hilk
          #4

          @mxyn as far as I understand it, that would be correct. however the update note function is super fast compared to canvas. I would first try everything inside one item


          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          M 1 Reply Last reply
          1
          • J.HilkJ J.Hilk

            @mxyn as far as I understand it, that would be correct. however the update note function is super fast compared to canvas. I would first try everything inside one item

            M Offline
            M Offline
            mxyn
            wrote on last edited by mxyn
            #5
            This post is deleted!
            1 Reply Last reply
            0

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved