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
