Test vram

This commit is contained in:
2026-03-10 13:08:26 -07:00
parent 1679b717db
commit 86603f015e
5 changed files with 329 additions and 0 deletions

View File

@@ -295,6 +295,10 @@ func _on_exit_interacted() -> void:
$target_scene.default_script(self) $target_scene.default_script(self)
``` ```
## Branching strategy
Do all work in a branch. Squash the branch before merging into master.
<!-- BEGIN BEADS INTEGRATION --> <!-- BEGIN BEADS INTEGRATION -->
## Issue Tracking with bd (beads) ## Issue Tracking with bd (beads)

View File

@@ -23,6 +23,7 @@ config/icon="res://icon.png"
ActionState="*res://ActionState.gd" ActionState="*res://ActionState.gd"
CameraTransition="*res://camera_transition.tscn" CameraTransition="*res://camera_transition.tscn"
VRAMMonitor="*res://measure_vram.gd"
[display] [display]

101
res:/measure_vram.gd Normal file
View File

@@ -0,0 +1,101 @@
extends Node
## VRAM/Texture Memory Measurement Tool
## Add this as an autoload to measure memory usage at startup
var measurements := {}
var start_time := 0.0
func _ready():
start_time = Time.get_ticks_msec()
# Initial measurement (before anything loads)
_record_measurement("initial")
# Wait for scene tree to be ready
await get_tree().process_frame
_record_measurement("after_tree_ready")
# Wait for main scene to fully load
await get_tree().process_frame
_record_measurement("after_main_scene_loaded")
# Give textures time to upload to GPU
await get_tree().process_frame
await get_tree().process_frame
_record_measurement("after_gpu_upload")
# Print final report
await get_tree().process_frame
_print_report()
func _record_measurement(label: String):
var elapsed_ms = Time.get_ticks_msec() - start_time
var tex_mem = Performance.get_monitor(Performance.VIEWER_TEXTURE_MEMORY)
var obj_count = Performance.get_monitor(Performance.OBJECT_COUNT)
var node_count = Performance.get_monitor(Performance.OBJECT_NODE_COUNT)
measurements[label] = {
"elapsed_ms": elapsed_ms,
"texture_memory_bytes": tex_mem,
"texture_memory_mb": tex_mem / (1024.0 * 1024.0),
"object_count": obj_count,
"node_count": node_count
}
print("[%.2f ms] %s: VRAM=%.2f MB, Objects=%d, Nodes=%d" % [
elapsed_ms / 1000.0,
label,
tex_mem / (1024.0 * 1024.0),
obj_count,
node_count
])
func _print_report():
print("\n" + "=" * 60)
print("VRAM USAGE REPORT")
print("=" * 60)
var total_elapsed = Time.get_ticks_msec() - start_time
for label in measurements:
var m = measurements[label]
print("\n[%s]" % label)
print(" Elapsed: %.3f s" % (m.elapsed_ms / 1000.0))
print(" Texture Memory: %.2f MB (%.2f KB)" % [m.texture_memory_mb, m.texture_memory_bytes / 1024.0])
print(" Object Count: %d" % m.object_count)
print(" Node Count: %d" % m.node_count)
print("\n" + "=" * 60)
print("TOTAL STARTUP TIME: %.3f s" % (total_elapsed / 1000.0))
print("=" * 60)
# Save to file if we have write access
var file_path = "user://vram_measurement_%s.txt" % Time.get_datetime_string_from_system(false, true).replace(":", "-")
var file = FileAccess.open(file_path, FileAccess.WRITE)
if file:
file.store_string("VRAM Measurement Report\n")
file.store_string("Generated: %s\n\n" % Time.get_datetime_string_from_system())
for label in measurements:
var m = measurements[label]
file.store_string("[%s]\n" % label)
file.store_string(" Elapsed: %.3f s\n" % (m.elapsed_ms / 1000.0))
file.store_string(" Texture Memory: %.2f MB\n" % m.texture_memory_mb)
file.store_string(" Object Count: %d\n" % m.object_count)
file.store_string(" Node Count: %d\n\n" % m.node_count)
file.close()
print("\nReport saved to: %s" % file_path)
## Call this manually at any point to get current VRAM usage
func get_current_vram_mb() -> float:
return Performance.get_monitor(Performance.VIEWER_TEXTURE_MEMORY) / (1024.0 * 1024.0)
## Get detailed breakdown
func get_memory_stats() -> Dictionary:
return {
"texture_memory_mb": get_current_vram_mb(),
"object_count": Performance.get_monitor(Performance.OBJECT_COUNT),
"node_count": Performance.get_monitor(Performance.OBJECT_NODE_COUNT),
"physics_objects_2d": Performance.get_monitor(Performance.PHYSICS_2D_OBJECT_COUNT),
"physics_objects_3d": Performance.get_monitor(Performance.PHYSICS_3D_OBJECT_COUNT),
}

223
tools/estimate_vram.py Normal file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/env python3
"""
Estimate VRAM usage by analyzing Godot's imported texture files.
This works without running the game.
"""
import os
import re
import json
from pathlib import Path
from collections import defaultdict
def parse_import_file(import_path):
"""Parse a .import file to get texture dimensions and format."""
dimensions = None
format_type = "unknown"
compressed = False
try:
with open(import_path, 'r') as f:
content = f.read()
# Look for dimension info in the import file
# Godot stores this in the remap section
dim_match = re.search(r'width=(\d+)', content)
height_match = re.search(r'height=(\d+)', content)
if dim_match and height_match:
dimensions = (int(dim_match.group(1)), int(height_match.group(2)))
# Check for compression
if 'ctex' in import_path or 'compress' in content.lower():
compressed = True
format_type = "compressed"
elif 'texture' in content.lower():
format_type = "rgba"
except Exception as e:
pass
return dimensions, format_type, compressed
def estimate_vram_size(width, height, format_type="rgba", compressed=False):
"""Estimate VRAM size in bytes."""
if compressed:
# BC7/DXT5 compression is roughly 4:1
return (width * height * 4) // 4
else:
# RGBA = 4 bytes per pixel
return width * height * 4
def scan_ego_resources(project_root):
"""Scan ego directory for all texture resources."""
ego_path = Path(project_root) / "ego"
if not ego_path.exists():
print("ERROR: ego/ directory not found")
return {}
textures = []
# Find all .import files
for import_file in ego_path.rglob("*.import"):
# Get the original PNG path
png_path = import_file.with_suffix('.png')
if png_path.exists():
# Get file size
file_size = png_path.stat().st_size
# Try to get dimensions from the import file
dimensions, format_type, compressed = parse_import_file(import_file)
# Estimate VRAM
if dimensions:
vram_bytes = estimate_vram_size(*dimensions, format_type, compressed)
else:
# Fallback: estimate from file size (compressed textures are smaller)
vram_bytes = file_size * 2 # Rough estimate
textures.append({
'path': str(png_path.relative_to(project_root)),
'file_size': file_size,
'dimensions': dimensions,
'vram_estimate': vram_bytes,
'compressed': compressed
})
return textures
def analyze_scene_resources(project_root, scene_path):
"""Analyze a scene file to count external texture resources."""
scene_file = Path(project_root) / scene_path
if not scene_file.exists():
return []
textures = []
with open(scene_file, 'r') as f:
content = f.read()
# Find all ext_resource entries for Texture2D
pattern = r'\[ext_resource type="Texture2D".*?path="([^"]+)"'
matches = re.findall(pattern, content)
for texture_path in matches:
full_path = Path(project_root) / texture_path
if full_path.exists():
textures.append(str(texture_path))
return textures
def main():
project_root = Path("/home/noti/dev/ai-game-2")
print("=" * 70)
print("VRAM USAGE ESTIMATOR (Static Analysis)")
print("=" * 70)
print()
# Analyze Ego resources
print("Scanning ego/ directory...")
ego_textures = scan_ego_resources(project_root)
if not ego_textures:
print("No textures found in ego/")
return
# Group by animation set
animation_sets = defaultdict(list)
for tex in ego_textures:
if 'minstrel-v3' in tex['path']:
animation_sets['minstrel-v3'].append(tex)
elif 'minstrel-v5' in tex['path']:
animation_sets['minstrel-v5'].append(tex)
elif 'rosella' in tex['path']:
animation_sets['rosella'].append(tex)
elif 'portrait' in tex['path']:
animation_sets['portrait'].append(tex)
elif 'mask' in tex['path']:
animation_sets['masks'].append(tex)
else:
animation_sets['other'].append(tex)
print(f"\nFound {len(ego_textures)} texture files in ego/")
print()
# Calculate totals
total_disk = 0
total_vram_compressed = 0
total_vram_uncompressed = 0
print("Breakdown by Animation Set:")
print("-" * 70)
for set_name, textures in sorted(animation_sets.items()):
if not textures:
continue
disk_sum = sum(t['file_size'] for t in textures)
vram_compressed_sum = sum(t['vram_estimate'] for t in textures)
vram_uncompressed_sum = sum(t['vram_estimate'] * 4 for t in textures)
total_disk += disk_sum
total_vram_compressed += vram_compressed_sum
total_vram_uncompressed += vram_uncompressed_sum
print(f"\n{set_name.upper()}:")
print(f" Files: {len(textures)}")
print(f" Disk Size: {disk_sum / (1024*1024):.2f} MB")
print(f" VRAM (compressed): {vram_compressed_sum / (1024*1024):.2f} MB")
print(f" VRAM (uncompressed): {vram_uncompressed_sum / (1024*1024):.2f} MB")
print()
print("=" * 70)
print("TOTALS:")
print("=" * 70)
print(f"Total Files: {len(ego_textures)}")
print(f"Total Disk Size: {total_disk / (1024*1024):.2f} MB")
print(f"Total VRAM (compressed): {total_vram_compressed / (1024*1024):.2f} MB")
print(f"Total VRAM (uncompressed): {total_vram_uncompressed / (1024*1024):.2f} MB")
print()
# Analyze main scene
print("Analyzing Game.tscn...")
game_textures = analyze_scene_resources(project_root, "Game.tscn")
print(f" External textures referenced: {len(game_textures)}")
# Analyze Ego scene
print("Analyzing Ego.tscn...")
ego_scene_textures = analyze_scene_resources(project_root, "Ego.tscn")
print(f" External textures referenced: {len(ego_scene_textures)}")
# Count unique minstrel-v3 frames
minstrel_v3_count = len([t for t in ego_textures if 'minstrel-v3' in t['path']])
print(f"\nMinstrel-v3 frames loaded at startup: {minstrel_v3_count}")
# Estimate startup VRAM
minstrel_v3_vram = sum(t['vram_estimate'] for t in ego_textures if 'minstrel-v3' in t['path'])
other_vram = total_vram_compressed - minstrel_v3_vram
print()
print("=" * 70)
print("STARTUP VRAM ESTIMATE (Compressed):")
print("=" * 70)
print(f"Ego (minstrel-v3 only): {minstrel_v3_vram / (1024*1024):.2f} MB")
print(f"Other ego resources: {other_vram / (1024*1024):.2f} MB")
print(f"Estimated room textures: ~5.00 MB (estimated)")
print(f"Estimated UI/cursors: ~1.00 MB (estimated)")
print(f"----------------------------------------")
print(f"TOTAL STARTUP VRAM: ~{(minstrel_v3_vram + other_vram) / (1024*1024) + 6:.2f} MB")
print()
print("UNCOMPRESSED estimate: ~{(minstrel_v3_vram + other_vram) * 4 / (1024*1024) + 24:.2f} MB")
print()
print("Note: These are estimates. Actual VRAM may vary based on:")
print(" - Godot's texture compression settings")
print(" - GPU driver optimizations")
print(" - Texture mipmap generation")
print("=" * 70)
if __name__ == "__main__":
main()