Godot: Polygon2D is extremely slow compared to Sprite rendering

Created on 3 Jul 2018  Â·  25Comments  Â·  Source: godotengine/godot

Godot version:
3.04

OS/device including version:
Windows 10 / Surface Pro 4

Issue description:
Polygon2D seems alarmingly slow when compared to Sprite or draw_rect().

Here are some results from a small test scene I made:
godot3_poly2d_performance

I've had the same results in a game project where I've attempted to create levels from Polygon2D objects. Usually optimized meshes would be very useful for optimizing fillrate, but this performance seems to make it impossible to utilize. I feel like this is going to hurt many graphically demanding 2D-projects.

Steps to reproduce:
Comparing between Sprite / Polygon2D and draw_rect() / draw_polygon() seems to give similiar results.

Minimal reproduction project:
polygon2d_performance.zip

bug rendering

Most helpful comment

I can confirm that this is still an issue; adding just 20 polygons was enough to tank my android performance. I managed to work around this problem by rendering the polygons in a viewport once, and using the resulting texture to draw the polygon with a sprite node instead. This entirely solved the problem for me, but wouldn't work if your polygon geometry changed every frame.

All 25 comments

Note: draw_polygon might require to recompute triangulation everytime, unless the polygon is retained and doesn't change (while sprites are trivial and dont require generic triangulation)

Note: draw_polygon might require to recompute triangulation everytime, unless the polygon is retained and doesn't change (while sprites are trivial and dont require generic triangulation)

Every time update() is called or every frame?
Still, that shouldn't concern the attached reproduction project since it generates individual nodes instead of using draw_polygon().

I also think for a few 2D sprites this is kind of slow in general. Though, I don't know the specs of OP's device.

It's not caused by triangulation in this case. A quick profiling shows that the multiple glBufferSubData and glDrawElements calls (from RasterizerCanvasGLES3::_draw_polygon) cause the FPS drop here.

I'm working on a PR that reduces the number of glBufferSubData calls by splitting canvas_render_items implementation into the following parts:

  1. Compute polygon data
  2. Send the polygon data to GPU (with a single glBufferSubData call per buffer)
  3. Execute render commands but use the already filled in buffers for rendering polygons.

Currently there is a per-polygon glBufferSubData call made and that hurts performance.

Some preliminary tests for the new approach seem to be promising (with ~4x FPS increase -> 240 vs 60 FPS).

did batching PR get merged in the end?

No, #20965 was merged but was then reverted in #21204.

I am having a big problem with this now.

What I need to do is to be able to submit triangles as vertices with uvs using canvas_item_add_triangle_array many times per frame. (I will have many nodes that each will draw themselves by using canvas_item_add_triangle_array.)

However doing so with less than 100 calls on iOS results in less than 20 fps. (It goes down to less than 10 fps when I do it in my game, compared to 60fps with out those calls.)

It is disappointing to see that Godot 3 does not do any automatic batching.

I would actually want to try and fix this .. or maybe at least write a new add_triangle_array call that would batch them, or maybe use something else that doesn't get impacted by the multiple glBufferSubData calls.

However, I would like to ask first ... why is iOS using only GLES3?

I would like to implement this in only GLES2 which I thought was the recommended mobile renderer, however iOS is hardcoded to use GLES3?

Just looking at #20077 speedup results:

Core i7-4790K, Intel 4600:

480 polygons: 325 vs 60 = 5,42
1920 polygons: 185 vs 19 = 9,74
7680 polygons: 77 vs 5 = 15,4
sprites: no change

Core i3-2310M, Intel 3000:

480 polygons: 60 vs 10 = 6
1920 polygons: 24 vs 3 = 8
7680 polygons: 7 vs 1 = 7
font rendering (using text_rendering.zip project attached below): 140 vs 10 = 14
sprites: no change

Android (Moto C):

font rendering: 57 vs 4 = 14,25

That is the difference between 2D polygons being actually useful or not.
I really wish that was merged, since 'full batching' hasn't been implemented and no one seems to even be working on it (?).

EDIT And looking at the code, it was a very well architected solution which would have been a great framework to later add batching. This and the other batching pull request, if combined into one would be perfect. (Then could optimize to reduce the number of flush calls to allow batching across canvas items.)

I don't know why there is such an all or nothing mindset though. reduz wants full batching so doesn't allow this PR even though it would be a good stepping stone. Then the batching PR gets completely reverted because of 9 patch rendering on Android instead of maybe just going back to old rendering for 9 patches?

is the dragonbones runtime outperforming godot's polygon2d node?
https://github.com/sanja-sa/gddragonbones/issues/5

I can confirm that this is still an issue; adding just 20 polygons was enough to tank my android performance. I managed to work around this problem by rendering the polygons in a viewport once, and using the resulting texture to draw the polygon with a sprite node instead. This entirely solved the problem for me, but wouldn't work if your polygon geometry changed every frame.

I'm not using Polygon2D but I think I'm having the same issue with canvas_item_add_triangle_array, with many vertices/lots of objects the performance can drop significantly even on desktop. For some time I thought that was physics drop performance but seems like the real bottleneck is rendering. 🤔

This should be vastly improved with the new Vulkan backend.

@kjav can you share a bit more details? something like this?:
Node
-sprite
-viewport
--polygon
on load, draw the polygon, save the viewport texture to the sprite, delete the viewport

I am not quite shure, whether this is part of the same issue... But using Custom StyleBoxFlat Ressources on Control Elements also impacts performance really bad. Just three elements drop the performance to 30-40fps on my S7 Edge. So this is basically unusable on mobile.

@dekaravanhoc Can you take screenshots of the StyleBoxFlat properties you've used? If using rounded corners, you may want to decrease their detail level. (Also, StyleBoxFlat doesn't rely on Polygon2D under the hood. It uses canvas_item_add_triangle_array as shown here.)

Shure. But I also changed the settings to test, whether transparency or anti-aliasing might be the reason, and neither of it is. Also the Label does not use rounded corners and just hits the performance as bad as the ProgressBar.

[sub_resource type="StyleBoxFlat" id=3]
bg_color = Color( 1, 1, 1, 0.784314 )
border_width_left = 8
border_width_top = 8
border_width_right = 8
border_width_bottom = 8
border_color = Color( 0, 0, 0, 0 )
corner_radius_top_left = 16
corner_radius_top_right = 16
corner_radius_bottom_right = 16
corner_radius_bottom_left = 16

[sub_resource type="StyleBoxFlat" id=4]
bg_color = Color( 0, 0.0980392, 0.0352941, 0.27451 )
border_width_left = 8
border_width_top = 8
border_width_right = 8
border_width_bottom = 8
border_color = Color( 0, 0.0901961, 0.0313726, 1 )
corner_radius_top_left = 16
corner_radius_top_right = 16
corner_radius_bottom_right = 16
corner_radius_bottom_left = 16

[sub_resource type="StyleBoxFlat" id=5]
bg_color = Color( 0, 0, 0, 0.588235 )
border_width_left = 8
border_width_top = 8
border_width_right = 8
border_width_bottom = 8
border_color = Color( 0, 0, 0, 0.784314 )
border_blend = true
expand_margin_left = 8.0
expand_margin_right = 8.0
expand_margin_top = 8.0
expand_margin_bottom = 8.0

Thats a simple ProgressBar:
image
image

Thats a Label:
image

I think I'm facing the same problem. I wrote some code which instantiates about 100 Polygon2D objects from a Polygon2D object scripted to mutate geometry in reaction to mouse movement and it's running incredibly slow...

@jjgh have you tried with 3.2? There was a PR merged that vastly improved polygon rendering.

Also try reducing rendering/limits/buffers/canvas_polygon_buffer_size_kb and rendering/limits/buffers/canvas_polygon_index_buffer_size_kb in the project settings

Thanks for your advice @clayjohn but unfortunately using 3.2 beta 3 did not make things better.
After a couple of seconds FPS drops to 8.
In my case, the geometry of those 100 Polygon2D objects is randomly modified at every frame where mouse movement is detected.
This random alteration often produces bad geometries ("bad polygon") which are not drawn and listed in the debugger. I'm not sure if this impacts on FPS.
I will try to somehow avoid painting bad geometries and see if it goes better.

@clayjohn changed the code to only draw triangles so that bad polygons aren't possible anymore and it now works really fast so I think I can confirm that in my case the slowness was only caused by the debugger activity.

@jjgh have you tried with 3.2? There was a PR merged that vastly improved polygon rendering.

Also try reducing rendering/limits/buffers/canvas_polygon_buffer_size_kb and rendering/limits/buffers/canvas_polygon_index_buffer_size_kb in the project settings

@clayjohn This worked very well for me! I'm using 3.2 to make a a polygon-heavy game and was stuck at ~25FPS with the default of 128Kb buffer for both values:

128kb_buffer

Switching both buffers to 32Kb moved this number up to ~40FPS!

32kb_buffer

FYI, this is the public 3.2 release running on a Moto X4 android device.

Thank you for all the work you and the other Godot developers have done. It's great to see such an active community.

For my use case, I was still seeing fairly bad performance after 3.2— I am rendering a large number of complex Polygon2D with many vertices. Using @kjav's suggestion above with @clayjohn's suggested logic for taking Viewport "snapshots" made a huge difference with my frame rate. As the latter mentions, figuring this out felt pretty unintuitive.

Here's a gist that renders an arbitrary polygon as a Sprite:
https://gist.github.com/jshorty/4090ba064ca0d3634d040e7a70c4833a

This will be fixed by https://github.com/godotengine/godot/pull/42119.

Running the example project at 1x, polygon 2D was about 75% of the speed of sprites, but at 2x or higher the speed was the exact same (likely because at that range it becomes fill-rate limited).

Fixed by #42119.

Was this page helpful?
0 / 5 - 0 ratings