Debugging or profiling choppiness / performance issues?

24 views
Skip to first unread message

Thomer Gil

unread,
May 12, 2020, 6:54:38 AM5/12/20
to excaliburjs
Is there a way to debug choppiness? Is there a structured way to profile performance issues? Thank you.

Erik Onarheim

unread,
May 12, 2020, 9:27:36 AM5/12/20
to Thomer Gil, excaliburjs
Hi Thomer,

The method we've used is profiling CPU with the browser profiling tools (chrome is pretty nice). It can help identify what's taking the longest time in the mainloop.

One thing that can happen over time is if actors aren't removed from the scene and build up, their update() is still called and can start to cause choppiness.

Another optimization that can be made is reducing the number of draw calls, and/or using images over fillRect or fillText which are slower than drawing an image.

What specifics are you seeing in your case?

Cheers,
Erik 

On Tue, May 12, 2020 at 5:54 AM Thomer Gil <thome...@gmail.com> wrote:
Is there a way to debug choppiness? Is there a structured way to profile performance issues? Thank you.

--
You received this message because you are subscribed to the Google Groups "excaliburjs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to excaliburjs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/excaliburjs/7b29438d-97e7-45c5-9fbc-ffe63a0f3419%40googlegroups.com.

Thomer Gil

unread,
May 12, 2020, 10:44:10 AM5/12/20
to Erik Onarheim, excaliburjs
Ah, very good! It appears to be the draw() function.

Two questions about that.
  1. How does one "reduce the number of draw calls"? Is that a setting on Engine?
  2. Part of my drawings never change, meaning I should really turn them into a sprite. Is it possible to "draw" a sprite? Meaning, can I "cache" part of a drawing and then re-use it as a sprite? The thing is: the drawing behavior very much depends on some configured constants. Turning part of my drawing into an image means I cannot easily change the constants later. So I'd like to be able to start out the game by drawing something, save it as a sprite, and then use that.
Thanks.



On Tue, May 12, 2020 at 3:27 PM, Erik Onarheim <erik.o...@gmail.com> wrote:
Hi Thomer,

The method we've used is profiling CPU with the browser profiling tools (chrome is pretty nice). It can help identify what's taking the longest time in the mainloop.

One thing that can happen over time is if actors aren't removed from the scene and build up, their update() is still called and can start to cause choppiness.

Another optimization that can be made is reducing the number of draw calls, and/or using images over fillRect or fillText which are slower than drawing an image.

What specifics are you seeing in your case?

Cheers,
Erik 

On Tue, May 12, 2020 at 5:54 AM Thomer Gil <thomer.gil@gmail.com> wrote:
Is there a way to debug choppiness? Is there a structured way to profile performance issues? Thank you.

--
You received this message because you are subscribed to the Google Groups "excaliburjs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to excaliburjs+unsubscribe@googlegroups.com.

Thomer Gil

unread,
May 12, 2020, 7:29:21 PM5/12/20
to Erik Onarheim, excaliburjs
So, in answer to my own question #2: yes, you can "cache" the result of draw() by turning it into a Sprite, but it's ugly and weird. And due to what appears to be an Excalibur bug I have not been able to test whether it yields a performance improvement.

I realized that my Paratrooper canon can be drawn in a limited number of ways only. After all, each angle of the turret gun has its own drawing. Let's call it 40 different positions? Therefore 40 different drawings. Using ctx.getImageData() followed by a ctx.putImageData() to an invisible, correctly sized canvas, and then calling ctx.toDataURL() on that canvas turns the drawing into an image, which can be loaded by a Texture and then turned into a Sprite. It's a horrible hack, but it may just work. Unfortunately, I cannot test this horrible idea yet due to https://github.com/excaliburjs/Excalibur/issues/1543 

Thomer Gil

unread,
May 13, 2020, 11:31:31 AM5/13/20
to Erik Onarheim, excaliburjs
Out of curiosity I implemented this "cache the result of draw() as a Sprite" scheme. The short of it is: it makes the code messy, fragile, with likely no meaningful performance gain.

The long version: after draw()-ing to the canvas (but still inside of draw()) I use ctx.getImageData() to grab the area of the canvas that I drew onto (while respecting Engine.pixelRatio). This ImageData I then render to another (invisible) canvas using putImageData(). I then invoke toDataURL() on the invisible canvas and pass the result to Texture's constructor. Invoking asSprite() on the Texture object yields a sprite. Voilà, (part of) the draw()ing turned into a Sprite.

You can then cache this Sprite object somewhere to later render it during update() in the right position (note: getImageData() uses top left coordinates, whereas an Actor uses the middle point) and at the right scale (because Engine.pixelRatio). I used Actor's addDrawing(cacheKey, sprite) and setDrawing(cacheKey) to store and render sprites. Obviously, update() needs to make sure it only bothers to change the sprite if the state of the Actor requires it.

Beware,though: when you override draw(), no call to update() will happen. They are mutually exclusive. If you're hoping to prevent an expensive draw() by doing an elegant Sprite render in update(), you'll need to make sure that draw() falls back to its default behavior by calling super.draw() and return from draw() if you have a hit in your custom sprite cache and want update() to take care of rendering the sprite.

I didn't do deep performance testing, but just eyeballing the browser's CPU usage, it made no meaningful difference. All in all, it's a no-good optimization. Learned a lot, though.
 


On Wed, May 13, 2020 at 1:29 AM, Thomer Gil <thome...@gmail.com> wrote:
So, in answer to my own question #2: yes, you can "cache" the result of draw() by turning it into a Sprite, but it's ugly and weird. And due to what appears to be an Excalibur bug I have not been able to test whether it yields a performance improvement.

I realized that my Paratrooper canon can be drawn in a limited number of ways only. After all, each angle of the turret gun has its own drawing. Let's call it 40 different positions? Therefore 40 different drawings. Using ctx.getImageData() followed by a ctx.putImageData() to an invisible, correctly sized canvas, and then calling ctx.toDataURL() on that canvas turns the drawing into an image, which can be loaded by a Texture and then turned into a Sprite. It's a horrible hack, but it may just work. Unfortunately, I cannot test this horrible idea yet due to https://github.com/excaliburjs/Excalibur/issues/1543 


Erik Onarheim

unread,
May 13, 2020, 11:56:19 AM5/13/20
to Thomer Gil, excaliburjs
I'll have some time today to take closer look at the performance to see if I can spot anything.

Do you have some source I could look at (read only is fine)?

To unsubscribe from this group and stop receiving emails from it, send an email to excaliburjs...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "excaliburjs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to excaliburjs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/excaliburjs/ka5hci52.bd52d7f8-2e8a-471a-b89f-1f32e441d84a%40we.are.superhuman.com.
Reply all
Reply to author
Forward
0 new messages