Performance and Batching
Keeping the draw call count low.
Shapes of each type are drawn in a single draw call, no matter how many there are.
Call Cost
Calling into the extension costs more than drawing the shape. Measured at 4000 discs a frame:
| µs per 1000 | |
|---|---|
discs_2d, one call | 66.8 |
disc_2d, color set once outside the loop | 107.5 |
disc_2d_colored, color passed in | 113.3 |
color = c then disc_2d, per shape | 219.3 |
Setting a property for every shape doubles the cost of that shape, so pass the color to the call instead. For large counts, use a bulk call, which crosses into the extension once.
Bulk Calls
Every flat shape has a bulk call, named as the plural, so discs_2d draws
many discs. It takes the same arguments as disc_2d, each as an array:
extends Node2D
func _process(_delta: float) -> void:
GeomDraw.reset_all_draw_states()
GeomDraw.color = Color(0.42, 0.85, 1.0)
var centers := PackedVector2Array()
centers.resize(288)
for i in 288:
centers[i] = Vector2(50 + (i % 24) * 24, 40 + (i / 24) * 24)
var radii := PackedFloat32Array()
radii.resize(1)
radii[0] = 8.0
var colors := PackedColorArray()
GeomDraw.discs_2d(centers, radii, colors)
using Godot;
public partial class Bulk : Node2D
{
public override void _Process(double delta)
{
Geom.Draw.ResetAllDrawStates();
Geom.Draw.Color = new Color(0.42f, 0.85f, 1.0f);
var centers = new Vector2[288];
for (int i = 0; i < 288; i++)
{
centers[i] = new Vector2(50 + (i % 24) * 24, 40 + (i / 24) * 24);
}
var radii = new float[1];
radii[0] = 8.0f;
var colors = new Color[0];
Geom.Draw.Discs2D(centers, radii, colors);
}
}

The arrays line up by index: the first center, radius and color make the
first disc, and so on. An array with one entry applies it to every disc, and
an empty colors array means the current color. Any other mismatch in
lengths is an error, and the call draws nothing.
A bulk call draws the same as calling the single version in a loop. It uses the same draw state and ends up in the same batches.
Batches
A batch holds shapes of one type, in one blend mode, drawn with one shader variant. A change in any of these results in a new batch:
- the blend mode
- dashed or not
- the shape type
- the texture
- the viewport
A thousand shapes that alternate between two blend modes take a thousand draw calls. The same shapes drawn in one mode for the first half and the other for the second take two.
Counting
queued_2d, queued_3d, draw_calls_2d and draw_calls_3d count the
current frame so far, and are cleared before the next frame starts. Read them
after your last draw call in _process. If there are many shapes and few draw
calls, batching is working.
Other Costs
- A concave polygon is triangulated every frame.
polygon_convex_onlyskips that, which is cheaper, but a concave polygon then draws wrong. primitive_detailmultiplies the vertex count of every sphere, cone and torus.- Round polyline joins add a disc per corner.
- A volumetric line is a tube of real geometry. A billboarded line is one quad.