godot-performance
Skill8 starsChanged 5 months agoMITView on GitHub
Performance optimization for Godot 4.x: profiling workflow (built-in profiler, CPU/GPU usage), memory management with ResourceCache and instance pooling, instancing optimization with packed scenes and LOD, render passes, draw calls, mobile-specific optimizations, Perfetto tracing, and nearest-neighbor viewport scaling. Use when debugging performance, optimizing games, or targeting low-end hardware.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- MindsMaster/godot-claude-code-skillstheir own version, first seen Oct 25, 2026
- MRCalderon3D/everything-game-dev-codetheir own version, first seen Oct 13, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- MRCalderon3D/everything-game-dev-code★ 105same name
- MindsMaster/godot-claude-code-skills★ 3same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- godot-47-migrationSkill
- godot-animationSkill
- godot-architectureSkill
- godot-audioSkill
- godot-brainstormingSkill
- godot-code-reviewSkill
- godot-csharp-patternsSkill
- godot-debuggingSkill
- godot-dialog-systemsSkill
- godot-gdscript-patternsSkill
- godot-headless-workflowSkill
- godot-i18nSkill
- godot-inputSkill
- godot-inventory-economySkill
- godot-networkingSkill
- godot-physicsSkill
- godot-project-setupSkill
- godot-save-systemsSkill
- godot-shaders-vfxSkill
- godot-state-managementSkill
- godot-uiSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 157
Use LOD to reduce rendering cost for distant objects.
- Very specific: line 173
Use MultiMeshInstance2D/3D for many similar objects.
- Distinctive: line 210
Stencil shadows: Use shadow maps instead of volumetric shadows when possible
- Distinctive: line 234
Use Mobile renderer instead of Forward+ for Android/iOS
- Distinctive: line 237
Use smaller shadow map sizes (512x512 or 256x256)
- Very specific: line 239
Use CanvasGroup to batch UI rendering
- Very specific: line 244
Avoid _get_node() in _process — use @onready instead
- Very specific: line 246
Use NavigationServer3D for pathfinding (avoids scene tree overhead)
- Very specific: line 248
Use PhysicsServer3D directly for bulk operations
- Distinctive: line 315
When the MCP bridge is running (Phase 2), these tools enhance performance debugging:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Performance Optimization for Godot 4.x
Systematic approach to profiling, optimizing, and debugging performance in Godot 4.x.
Profiling Workflow
Built-in Profiler
Access the profiler via Debug > Start Profiling or press Ctrl+Shift+P.
# Enable profiling in your project settings:
# Project Settings > Debug > Profile Every Frame = true
# Use time measure for specific code sections
var start_time = Time.get_ticks_usec()
# ... expensive operation ...
var elapsed = Time.get_ticks_usec() - start_time
print("Operation took: ", elapsed, " us")CPU/GPU Usage Monitoring
# Display FPS and frame time in debug overlay
# Project Settings > Debug > Show FPS = true
# Monitor specific metrics
func get_performance_stats():
var stats = {
fps: Performance.get_monitor(Performance.TIME_FPS),
frame_time: Performance.get_monitor(Performance.TIME_FRAME_DURATION),
draw_calls: Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME),
active_objects: Performance.get_monitor(Performance.OBJECT_COUNT),
mem_alloc: Performance.get_monitor(Performance.MEMORY_STATIC),
mem_pool: Performance.get_monitor(Performance.MEMORY_STATIC_SPECIALIZATION)
}
return statsProfiling Checklist
- Start with a clean build — Profile the exported release build, not the debug version
- Enable all profiler categories — CPU, GPU, Physics, Rendering, Audio
- Record a representative gameplay segment — 30-60 seconds of typical play
- Identify hotspots — Look for functions consuming >5% of frame time
- Profile before and after changes — Measure impact of each optimization
Memory Management
ResourceCache Pattern
Preload and cache frequently-used resources.
class_name ResourceCache extends Node
static var _cache := {}
static func preload_resource(path: String) -> Variant:
if not _cache.has(path):
var res = load(path)
if res:
_cache[path] = res
else:
push_warning("Failed to load resource: " + path)
return _cache[path]
static func get_scene(path: String) -> PackedScene:
return preload_resource(path) as PackedScene
# Usage
var bullet_scene = ResourceCache.get_scene("res://scenes/entities/bullet.tscn")
var bullet = bullet_scene.instantiate()Instance Pooling Pattern
Reuse objects instead of creating/destroying them.
class_name ObjectPool<T> extends Node where T is RefCounted
@export var pool_size: int = 20
var _pool: Array[T] = []
var _active: Array[T] = []
func _ready():
for i in range(pool_size):
var instance = create_instance()
instance.set_process_mode(PROCESS_MODE_ALWAYS)
add_child(instance as Node)
_pool.append(instance as T)
func get_free() -> T:
if _pool.size() > 0:
var obj = _pool.pop_back()
obj.set_active(true)
_active.append(obj)
return obj
return null
func release(obj: T) -> void:
obj.set_active(false)
_active.erase(obj)
_pool.append(obj)Instancing Optimization
Packed Scene Best Practices
# preload() for always-needed scenes (compile-time)
const BulletScene = preload("res://scenes/entities/bullet.tscn")
func spawn_bullet():
var bullet = BulletScene.instantiate()
add_child(bullet)
# load() for conditional scenes (runtime)
func load_level(level_name: String):
var path = "res://levels/" + level_name + ".tscn"
var scene = load(path) # Cached by Godot internally
if scene:
current_level = scene.instantiate()
add_child(current_level)Level of Detail (LOD)
Use LOD to reduce rendering cost for distant objects.
class_name LODObject extends Node3D
@export var lod_distances: PackedFloat32Array = [10.0, 30.0, 60.0]
@onready var lod_nodes = [$HighDetail, $MediumDetail, $LowDetail]
func _process():
var dist = global_position.distance_to(get_viewport().get_camera3D().global_position)
for i in range(lod_nodes.size()):
lod_nodes[i].visible = dist < lod_distances[i]Instanced Rendering
Use MultiMeshInstance2D/3D for many similar objects.
# Draw 1000 identical trees with a single draw call
var mesh = preload("res://models/tree.tscn")
var multi_mesh = MultiMesh.new()
multi_mesh.mesh = mesh.resource
multi_mesh.transform_format = MultiMesh.TRANSFORM_3D
multi_mesh.instance_count = 1000
for i in range(1000):
var x = randf() * 200 - 100
var z = randf() * 200 - 100
multi_mesh.set_instance_transform(i, Transform3D(Basis(), Vector3(x, 0, z)))
$MultiMeshInstance3D.multi_mesh = multi_meshRender Passes and Draw Calls
Minimizing Draw Calls
# BAD: Many small sprites (many draw calls)
for i in range(100):
var sprite = Sprite2D.new()
sprite.texture = grass_texture
add_child(sprite)
# GOOD: Use TileMap or MultiMesh (single draw call)
# TileMap handles batching automatically
# Or use a custom mesh with instanced renderingRender Pass Optimization
- Forward+ renderer: Better for many lights, use for 3D games
- Mobile renderer: Fewer passes, better for low-end devices
- Stencil shadows: Use shadow maps instead of volumetric shadows when possible
- LightmapGI: Bake static lighting to avoid real-time shadows
# Set renderer in Project Settings > Rendering > Renderer
# Forward Plus: Better quality, higher cost
# Mobile: Optimized for low-end hardwareMobile-Specific Optimizations
Texture Compression
# Project Settings > Export > Android/iOS
# Set texture format:
# - ASTC: Best quality/size ratio (modern devices)
# - ETC2: Fallback for older Android devices
# - DXT: Desktop only
# Use compressed textures in the editor:
# Texture > Compressed = TrueMobile Rendering Tips
- Use Mobile renderer instead of Forward+ for Android/iOS
- Limit active lights per object (use light masks)
- Bake static lighting with LightmapGI
- Use smaller shadow map sizes (512x512 or 256x256)
- Disable post-processing effects when not needed
- Use
CanvasGroupto batch UI rendering - Set
texture_filtertoTEXTURE_FILTER_NEARESTfor pixel art
CPU Optimization for Mobile
- Avoid
_get_node()in _process — use@onreadyinstead - Minimize
_instantiate()calls — use object pooling - Use
NavigationServer3Dfor pathfinding (avoids scene tree overhead) - Batch physics queries instead of individual raycasts
- Use
PhysicsServer3Ddirectly for bulk operations
Godot 4.7 Performance Features
Perfetto Tracing for Android (Godot 4.7)
Perfetto provides detailed profiling data for Android builds.
# Enable in Project Settings > Debug
# Perfetto tracing captures:
# - Frame timing breakdown
# - GC pauses
# - Thread scheduling
# - GPU command queue
# After export, run:
# godot --perfetto-trace output.perfetto
# Then open in https://ui.perfetto.dev/Nearest-Neighbor Viewport Scaling (Godot 4.7)
Pixel-perfect scaling for retro-style games.
# Project Settings > Display > Window > Stretch Mode
stretch_mode = CanvasGroup.STRETCH_MODE_SCALE
stretch_aspect = CanvasGroup.STRETCH_ASPECT_KEEP
# Godot 4.7: Use nearest-neighbor scaling for crisp pixel art
texture_filter = TextureFilter.NEAREST
# Calculate integer scale factor
func get_viewport_scale() -> int:
var target_width = 320 # Original game resolution
return max(1, int(DisplayServer.window_get_size().x / target_width))Performance Checklist
Before Release
- Profile the release build (not debug)
- Check FPS stays above 30 (60 ideal) on target hardware
- Verify memory usage stays within device limits
- Test on lowest-spec target device
- Check for GC spikes (>1ms pauses)
- Verify texture compression is enabled
- Reduce draw calls with instancing
- Use lightmaps for static scenes
- Implement object pooling for frequently spawned entities
Runtime Monitoring
# Add to debug HUD
class_name PerfHUD extends CanvasLayer
@onready var fps_label = $FPSLabel
@onready var mem_label = $MemLabel
func _process(delta):
fps_label.text = "FPS: " + str(Performance.get_monitor(Performance.TIME_FPS))
mem_label.text = "Mem: " + str(Performance.get_monitor(Performance.MEMORY_STATIC) / 1024 / 1024) + " MB"MCP Bridge Tools (Optional — Live Editor Integration)
When the MCP bridge is running (Phase 2), these tools enhance performance debugging:
profiler_start— Start a profiling session from the editorprofiler_stop— Stop and display resultsmemory_stats— View current memory allocation breakdownrender_stats— Query draw calls, triangles, and active texturesperfetto_export— Export Perfetto trace for Android debugging
Note: MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.
References
- Profiler Documentation
- Memory Management Patterns
- Mobile Optimization Checklist
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

