Jump to content

Performance issues with lots of Canvas2D objects


thrice
 Share

Recommended Posts

I am having performance issues (bad enough to make the game unplayable), when using the canvas 2d library. I am wondering if there are better ways to do what I am currently (surely there must be). For context: building a card game.

The issue manifests itself as soon as I tried using canvas2d with my card object. Each players deck consists of 60 cards, so there's at least 120 parent card meshes. The following code is what's causing the slow down.

      this.babylon = this.$children.plane.babylon;
      this.babylon.parent = this.parentPlane.babylon;
      this.babylon.position = new BABYLON.Vector3(0, -0.2, 0);
      this.babylon.material = new BABYLON.StandardMaterial("attribute_game_text", this.scene.babylon);

      this.canvas = new BABYLON.WorldSpaceCanvas2D(this.scene.babylon, new BABYLON.Size(150, 150), {
        id: "PlaneCanvas",
        cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_CANVAS,
        customWorldSpaceNode: this.babylon,
        backgroundFill: "transparent",
        isPickable: false,
        children:
        [
          new BABYLON.Text2D(this.card.gameText() || " ", {
            defaultFontColor: new BABYLON.Color4(0, 0, 0, 1),
            marginAlignment: "h:center, v:center",
            fontName: "16pt Helvetica",
            fontSuperSample: false
          })
        ]
      });

In example above, this.babylon is a plane.

 

I was using tracked node, but then I found an example setting customWorldSpaceNode per above, and that seemed like the ideal way for me to do things since I am trying to render text atop the plane which exists in my game (and tracked node brings all text to the top which I'd like  to avoid).

 

Anyways, my question is:

Is there some obvious setting I am missing that could speed it up, or is this a bad idea to instantiate that many WorldSpaceCanvas2d nodes in general? (it sounds like an expensive object, however I would think the caching strategy would be preventing the performance issues I am seeing. Or anyone have any alternate/better ideas?

 

 

Link to comment
Share on other sites

YES ! (well, I think you will not share my happy thoughts...) but I'm glad I'm having performance issues reported! :)

I'm going to help you as much as I can to solve this, perf are top priority for me.

But first, I need more info about what you're trying to achieve and why you chose the design you came up with.

Here are the questions:

1) why you need to rely on WorldSpaceCanvas (WSC) instead of Screen Space? 

2) what kind of content do you display in these canvas?

I know there's currently a big perf improvement I can make for (WSC) rendering, because right now if you have 120 WSC it will end up with 120 Draw Calls to render the 120 planes. But I could rely on InstancedMesh (when you use the default WSC Node, which is a Plane of a given size) to greatly reduce the Draw Calls. If you create 120 WSC with the same size it would end up with 1 draw call at best, and maybe few more at worst (I can explain that if you want).

 

Link to comment
Share on other sites

@Nockawa haha I've never met anyone so excited to debug performance issues, I admire your eagerness to do so.

So first things first: it was largely an ID10T error on my part, the jist of it is:

I essentially have hover states attached to my cards, which trigger a zoom animation on a copy of the card to show the card being hovered. - When I converted over to canvas2d, the text was blocking the plane below which had the hover states, in certain small areas, which was leading to the hover animation being called pretty much everytime the mouse was being moved even a pixel. So tl;dr; , by setting enableInteraction to false, along with throttling the hover, it largely solved my performance issues.

That being said, it's rendering around 45-59 now, so still not 60, but truthfully I don't fully remember if it was at 60 before switching to canvas2d.

The reason I am creating so many world space canvases, is something I am still struggling with. Basically, I am trying to figure out a predictable way of rendering text within a corresponding plane of a certain shape. You can see my issue easier than I can explain: (disclaimer, using placeholder images)

Screenshot%202017-02-22%2020.18.30.png?r

 

So basically each card has potentially 4 attributes, a name, and a description. I am trying to position the attributes at each corner of the 3d plane, and the description on the bottom part of the card. So: I am using the world space canvas with customWorldSpacenode to paint the text attributes transparently over the top of the 3d plane (which has actions and what not attached), because the size of the 2d canvas per card needs to change, depending on the size of the screen. So I have a bunch of methods on my plane controller for attempting to get the size of the card plane on screen, then I am doing the following to get to what you see in the screenshot

      plane.material = new BABYLON.StandardMaterial("transparent_background", this.scene.babylon);
      plane.material.diffuseTexture = new BABYLON.DynamicTexture("transparent_texture", this.parentPlane.sizeOnScreen().height, this.scene.babylon, true);
      plane.material.diffuseTexture.hasAlpha = true;

      this.canvas = new BABYLON.WorldSpaceCanvas2D(this.scene.babylon, new BABYLON.Size(this.parentPlane.sizeOnScreen().width, this.parentPlane.sizeOnScreen().height), {
        id: "PlaneCanvas",
        cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_CANVAS,
        customWorldSpaceNode: this.$children.plane.babylon,
        backgroundFill: "transparent",
        isPickable: false,
        enableInteraction: false,
        children:
        [
          new BABYLON.Text2D(this.cost().toString(), {
            marginAlignment: "h:left, v:top",
            fontName: "8pt Arial",
            fontSuperSample: true,
            y: -2,
            x: 8
          }),
          new BABYLON.Text2D(this.fate().toString(), {
            marginAlignment: "h:right, v:top",
            fontName: "8pt Helvetica",
            fontSuperSample: true,
            y: -2,
            x: -8
          }),
          new BABYLON.Text2D(this.health().toString(), {
            marginAlignment: "h:right, v:bottom",
            fontName: "8pt Helvetica",
            fontSuperSample: true,
            y: 2,
            x: -8
          }),
          new BABYLON.Text2D(this.attack().toString(), {
            marginAlignment: "h:left, v:bottom",
            fontName: "8pt Arial",
            fontSuperSample: true,
            y: 2,
            x: 8
          }),
          new BABYLON.Text2D(this.card.gameText() || " ", {
            defaultFontColor: new BABYLON.Color4(0, 0, 0, 1),
            marginAlignment: "h:center, v:top",
            fontName: "4pt Helvetica",
            fontSuperSample: true,
            y: -39
          }),
          new BABYLON.Text2D(this.card.name, {
            // defaultFontColor: new BABYLON.Color4(0, 0, 0, 1),
            marginAlignment: "h:center, v:top",
            fontName: "4pt Helvetica",
            fontSuperSample: true,
            y: -33
          }),
        ]
      });

As you can however see from the screenshot, things aren't working terribly well. Positions are still off slightly, and in order to use the actual screen size of the plane for the width/height, I am having to set the font sizes extremely small which results in things looking pretty bad.

I guess I am wondering if you have any better ideas as how to structure things. I guess, the root of my struggles come down to having to set a plane size in babylon that is a relative percentage, and then having to create canvases in pixels which are absolute.

Link to comment
Share on other sites

Ok, so, let's do this step by step

If you need text to be displayed following the plane a of 3D object, so not following the plane of the viewport: then a WorldSpaceCanvas (WSC for short) must be used. So I think you made the right decision here.

Quote

So: I am using the world space canvas with customWorldSpacenode to paint the text attributes transparently over the top of the 3d plane (which has actions and what not attached)

Right! But then you said:

Quote

..., because the size of the 2d canvas per card needs to change, depending on the size of the screen.

there you lost me. Why the size of the WSC is related to the size of the screen?

Quote

As you can however see from the screenshot, things aren't working terribly well. Positions are still off slightly, and in order to use the actual screen size of the plane for the width/height, I am having to set the font sizes extremely small which results in things looking 

First, use the latest preview version I've commit 1-2 days ago (see this post, an you should follow its thread). Alignment/Positioning is way more reliable now.
Now concerning your "extremely small", yes, that's something I've realized few weeks ago, when you set the size of your WSC it's in pixel but it must match the 3D World Coordinates and something (well, most of the time I should say) these two coordinates systems are totally unrelated. But you need to keep the ratio between the 3D world plane and the WSC that is created. So I've introduced lately the "unitScaleFactor" setting at the WSC's construction, to apply a scale factor on the WSC that will be created.

Example: you need a WSC to be 40x80 in 3D world coordinates (for instance this is the size of your 3D card), but in order to display text correctly you need a bigger surface, if it's something like 120*240 then you have to set a unitScaleFactor of 3. This way you're dealing with a WSC that is "more suited" for text rendering/fontsize.

Quote

I guess I am wondering if you have any better ideas as how to structure things. I guess, the root of my struggles come down to having to set a plane size in babylon that is a relative percentage, and then having to create canvases in pixels which are absolute.

I hope that my answer above is the solution to this. tell me if I'm correct.

About Performances

Because it's like size, it matters after all, no matter what they say! If your WSC's content don't change (in your case if the text is pretty much static) it won't be rendered... To say it more clearly: the WSC's is rendered only if there are changes made. So if there're none, it's rendered one time for good.
Then after the deal is it will add one draw call to draw your custom node. If you have 120 custom node, then it's 120 Draw Calls, I don't think it would slow things down that much. but if it's a concern and that you manage to make all your card's WSC with the same size, then you should use Mesh.createInstance() to create a InstancedMesh in order to make your 120 Draw Calls just a single one!

But first thing first: quality/fidelity, then perf. So let's solve the positioning/text quality first.

One last thing, once the font size you set is pretty good (at least 14px) try to use the signedDistanceField mode instead of the superSample one.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...