#!/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()