slowdown when drawing lines for >100objects in canvas

  • I made a directed graph conversation editor, it's working flawlessly, apart from one thing:

    after I create more than 100 9-patch objects (dialogue nodes), that is about 824 objects, along with their contained objects (buttons, text), everything becomes sluggish. I get: 416 collisions per tick and 130 poly checks per tick, which doesn't sound huge. FPS drops to 9, CPU usage becomes 86% whenever I create a new one and goes back to 60 and 2% if I leave it alone.

    The project is using R0J0hound 's Canvas plugin, to render the directional arrows every time the user clicks, which is super fast up to a point. But maybe calculating arrows for more than 100 pairs of coordinates is too much. Is there a smart way to just calculate changed nodes, instead of all 100 at once? Perhaps a 2-step process of clearing the canvas?

    Thanks in advance!

    Here's the CAPX: https://dl.dropboxusercontent.com/u/28087823/LD34%20LandsOfLore/ConversationEditor/conv%20editor6.capx

    Here it is in action:

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Using the draw line feature of the Canvas plugin is definitely what's killing performance here, especially since you have to redraw every line whenever something updates. There may be ways around erasing the whole canvas, but no matter what you'll have to redraw everything when you zoom in and out.

    My suggestion would be to use Paster instead of canvas, which properly supports WebGL. It doesn't have line drawing support, but you should be able to get around this by pasting a rotated and stretched sprite.

    EDIT: Upon further investigation, I take back everything -- will investigate more.

  • I'll have to look at your capx later when I'm home but here's a few thoughts:

    If webgl is enabled I'd still recommend using the paster object because the canvas has to be copied to a webgl texture every tick that it changes. And actually the text object does the same thing so maybe you could use Spritefonts instead.

    Actually if that is the bottleneck you can quickly check by setting webgl to off in the project settings. If the performance increases and the cpu usage goes down then I'd just leave webgl off, as it eliminates the need to copy the texture of the canvas and text instances.

    Another thought is unless you're clicking or constantly checking what the mouse is over shouldn't the collision checks per second be 0? I guess I'll see when I open the capx.

  • 9 patch is like 9 objects in one.

  • newt

    Not really. I mean you can think of it as a combination of sprites and tiled backgrounds when it's drawn, so as far as draw calls, maybe. In reality it's probably less, but that's not what's happening here IMO.

  • R0J0hound

    Thats spot on. Just tested, spawning eats up fps, but it goes right back up to 60 even with hundreds of objects.

    Guess there were some improvements I missed.

  • Thanks everyone for taking the time <3

    Performance is the same with or withoug WebGL.

    I am making a few collision calls, mainly "cursor is over" and "cursor is not over". I don't know how to eliminate those while keeping the system intact. I now suspect spawning each node (1 container and 6 other objects) is too much for C2 after 100 have been already spawned.

    I'm replacing canvas with Paster , and will report back

  • Hm other things that are unoptimized, working on them now:

    1. every time I call for a canvas redraw, I ask for every node to be iterated. I should only request that children nodes that are on the screen be iterated. This doesn't help if there's 100 nodes on-screen, but does help when the editor is properly used, i.e. there's about 20 nodes on-screen.

    2. each time I make a change, I make canvas reposition itself at the topLeft viewport. Maybe that takes a lot of processing power?

    I also resize the canvas to WindowWidth/ layoutScale and windowHeight/layout scale, so that it's always as large as the window, regardless of zoom level. Maybe that's very calculations-intensive as well?

    EDIT: The exact same thing happens with Paster, no improvement. Canvas isn't to blame.

    When I remove all Text objects, also no improvement.

    I think I may be making some rookie mistake with loops, or referencing objects that are off screen? :S

    I don't get where all the collisions are coming from either, since collision checks are very strictly defined, and I've disabled most objects' collisions. Made all the optimization I knew how to make, updated the above CAPX file. Still, nothing.

  • Paster would only make a difference if webgl is used, and even then with one instance of the canvas it's not going to effect a whole lot.

    Had a look at the capx. Nothing stands out as a problem. For me the debugger eats up the most of the cpu so maybe you could just add a fps counter to your game/app to get a better idea of the perfomance.

    You're not running into an issue of drawing too much, at least not with 100 of your dialogs. Even the line drawing isn't causing much of a slowdown. I disabled drawing them and didn't notice much of a difference.

    The collision checks are caused by the "overlapping", "on object clicked" and drag n drop behavior. I don't really see any place you can reduce those in your capx. You could reduce the amount of checks if you did something like the following so the top dialog and it's container are checked for hover and click events. If the click is elsewhere then the rest of the dialogs are checked. This should reduce the normal cpu usage. One caveat is hover effects only apply to the top dialog, and you may want it to work with everything.

    pick top dialog
    mouse over dialog?
    --- do stuff
    
    on click
    	local number case=0
    
    	pick top dialog
    	mouse over dialog?
    	--- set case to 1
    		mouse over button1
    		--- do stuff
    		mouse over button2
    		--- do stuff
    	case = 0
    	mouse over dialog?
    	pick top dialog
    	--- set case to 1
    	--- move to top
     
    	case = 0
    	--- //clicked on empty space[/code:o6q4mgap]
    
    What the most likely cause for the slowdown is the high object count and the picking of events.  If you can reduce the number of times you have to filter the SOL for an object.  Not always possible but utilizing sub-events instead of picking from "all" can help.
    
    The first culprits of events that slow things down are nested loops.  In your "update canvas" function, it loops over each dialog and then each of it's children. One thing you could do to speed things up is to store the children's uids in the list so you can then use "pick by uid".  Picking by uid basically picks the object directly, whereas using a comparison casuses all the objects to be checked.
    A second idea would be to not store a list of the children but instead only store the uid of the parent.  This would require reworking your event sheet but the result is much simpler.  As a note if the dialog doesn't have a parent set the variable to -1.
    [code:o6q4mgap]local number endx=0
    local number endy=0
    local number parent=-1
    
    for each dialog
    --- set endx to dialog.x
    --- set endy to dialog.y
    --- set parent to dialog.parent
    	pick all dialog
    	pick dialog by uid parent
    	--- drawline from (dialog.x,dialog.y) to (endx,endy)[/code:o6q4mgap]
    
    That way each dialog is looped over once and it's connected parent is picked directly. should be much faster.
  • R0J0hound so I'm not making super stupid mistakes? That's a huge relief. But your suggestions are still genius, thank you for going into the trouble to check all that undecipherable code! I'll implement right away and report back <3

    Such an amazing performance boost, just from changing the picking system. you're the best. I've got 300 nodes and going! One tiny more thing now: zooming in and out still has a huge performance hit. Resizing and re-positioning the canvas seems to be very intensive. I made zooming less smooth (only three levels of zoom, you go to each one by rolling the mouse wheel, and that's it). But even then, I don't like the FPS drop spike, seems unstable

    Here's the CAPX with my changes, for future reference of anyone who's interested:

    https://dl.dropboxusercontent.com/u/28087823/LD34%20LandsOfLore/ConversationEditor/conv%20editor7.capx

  • christina

    Glad it helped the performance.

    Changing the position and size of the canvas wouldn't affect the performance. What would affect it, I imagine, is the "resize canvas" action, which changes the texture size. If you can i'd leave the texture size be, and just change the object size. The only issue is the positions would be off when you zoom in and out because the line draw commands are relative to the texture, not the object. This doesn't apply for the "paste" action though, it will paste in place regardless.

    Typically when the texture size is the same as the object size you can do this to map layout positions to the canvas:

    sprite.x-canvas.left

    sprite.y-canvas.top

    When the sizes are different you can do this instead:

    (sprite.x-canvas.left)*textureWidth/canvas.width

    (sprite.y-canvas.top)*textureHeight/canvas.height

    I didn't seem to include a way to read the texture width and height so you'll want to save them to variables. Just note when the canvas object is created the texture size will be the same as the object size. Then you if you ever use the "resize canvas" action you'll want to update those variables with the new size.

  • R0J0hound avoiding canvas resize didn't help unfortunately. Or it wasn't noticeable anyway, and the cost was that the code became a lot less readable with all the coordinate transformations.

    But I did realize, I don't need continuous canvas redraws when I'm zooming! So I'm now only requesting a resize+redraw when layout scale is 1, 0.75 and 0.5. As you can imagine performance is stellar.

    Thanks for everything <3

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)