Smarter Card Rendering in Godot: 2.6x FPS Boost!

π Dev Log: Smarter Card Rendering in Godot β 2.6x FPS Boost!
By Antony Qin, Lead Gameplay Engineer @ Two Robots
TL;DR
We just rolled out a major optimization to how cards are rendered in our Godot-based game client β and the results are wild: FPS jumped from 70 β 106, and node count was cut nearly in half.
This upgrade was all about replacing our old card loader with a smarter, memory-conscious system that uses texture caching and reference counting to avoid redundant scene creation.
Let me walk you through what changed π
π§ The Problem: Redundant Scene Creation
In the old setup, every time a card needed to appear in the game β whether on the board, in your hand, or even while hovering over the graveyard β we would:
- Instantiate a brand-new scene.
- That scene was a
CardDynamicOverlay
, which contains ~70 nodes (text, images, backgrounds, effects, etc). - If two players each had two copies of the same card (
card_scrap_arms_solar
), that added up to:2 players Γ 2 cards Γ 70 nodes = 280 nodes
- Even though every card looked exactly the same!
Multiply that by all the unique cards on screen, and we were paying the price in performance, memory, and video overhead.
π§ The Solution: One Scene, Many Textures
We rewrote the entire system.
Instead of constructing a new node tree for every card, we now use one single CardDynamicOverlay
instance for the whole game. This is managed by a new CardImageManager
, which:
- Builds a card texture in a background thread using backend data.
- Renders the texture from the overlay scene.
- Saves it and returns it to any object that needs it.
- Reuses it for all future requests of that card (unless the card has dynamic properties like cost changes).
- Cleans it up when itβs no longer needed, using reference counting.
This way, visually identical cards share the same texture β drastically reducing memory usage and node counts.
π¬ The Impact
Hereβs a snapshot of the Godot debugger with the old vs new method:

Before:
- 25,595 objects
- 10,489 nodes
- 3,488 draw calls
- FPS: 70
- Video memory: 5.24 GiB
After:
- 13,581 objects
- 5,601 nodes
- 1,728 draw calls
- FPS: 106
- Video memory: 4.88 GiB
In other words, we reduced:
- Objects by ~47%
- Draw calls by ~50%
- Nodes by nearly half
- While boosting FPS by 51%
π Whatβs Next?
Weβve already shipped this improvement to the Windows build. If you're on the playtest list, you'll notice the smoother experience right away β especially when loading into the pick cards screen.
Special thanks to @Shambel and others for testing across different machines. This change should especially help users on lower-spec rigs.
More to come soon.
β Antony