|
|
""" |
|
|
Performance optimization utilities for Manim rendering |
|
|
""" |
|
|
|
|
|
import re |
|
|
from typing import Dict, Tuple |
|
|
|
|
|
class PerformanceOptimizer: |
|
|
"""Optimize rendering performance and resource usage""" |
|
|
|
|
|
|
|
|
QUALITY_SETTINGS = { |
|
|
"low_quality": { |
|
|
"flag": "-ql", |
|
|
"description": "Low Quality (Fast)", |
|
|
"resolution": "480p", |
|
|
"fps": 30, |
|
|
"max_duration": 10, |
|
|
"max_objects": 20, |
|
|
"performance_threshold": { |
|
|
"render_time": 60, |
|
|
"memory_usage": 1024 |
|
|
} |
|
|
}, |
|
|
"medium_quality": { |
|
|
"flag": "-qm", |
|
|
"description": "Medium Quality (Balanced)", |
|
|
"resolution": "720p", |
|
|
"fps": 30, |
|
|
"max_duration": 15, |
|
|
"max_objects": 30, |
|
|
"performance_threshold": { |
|
|
"render_time": 180, |
|
|
"memory_usage": 2048 |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
PERFORMANCE_THRESHOLDS = { |
|
|
"render_time": 300, |
|
|
"memory_usage": 4096, |
|
|
"cpu_usage": 80 |
|
|
} |
|
|
|
|
|
@classmethod |
|
|
def estimate_render_time(cls, quality: str, complexity: str = "medium") -> int: |
|
|
"""Estimate rendering time in seconds""" |
|
|
base_times = { |
|
|
"low_quality": 30, |
|
|
"medium_quality": 60, |
|
|
"high_quality": 120, |
|
|
"production_quality": 300 |
|
|
} |
|
|
|
|
|
complexity_multipliers = { |
|
|
"simple": 0.5, |
|
|
"medium": 1.0, |
|
|
"complex": 2.0, |
|
|
"very_complex": 3.0 |
|
|
} |
|
|
|
|
|
base_time = base_times.get(quality, 60) |
|
|
multiplier = complexity_multipliers.get(complexity, 1.0) |
|
|
|
|
|
return int(base_time * multiplier) |
|
|
|
|
|
@classmethod |
|
|
def analyze_code_complexity(cls, code: str) -> str: |
|
|
"""Analyze code complexity to estimate render time""" |
|
|
complexity_indicators = { |
|
|
"simple": ["Text", "Write", "Create"], |
|
|
"medium": ["Transform", "FadeIn", "FadeOut", "MoveTo"], |
|
|
"complex": ["Axes", "plot", "FunctionGraph", "BarChart"], |
|
|
"very_complex": ["ThreeDScene", "rotate", "complex", "integral"] |
|
|
} |
|
|
|
|
|
code_lower = code.lower() |
|
|
scores = {"simple": 0, "medium": 0, "complex": 0, "very_complex": 0} |
|
|
|
|
|
for complexity, indicators in complexity_indicators.items(): |
|
|
for indicator in indicators: |
|
|
scores[complexity] += code_lower.count(indicator.lower()) |
|
|
|
|
|
|
|
|
max_score = max(scores.values()) |
|
|
if max_score == 0: |
|
|
return "simple" |
|
|
|
|
|
return max(scores, key=scores.get) |
|
|
|
|
|
@classmethod |
|
|
def get_recommended_quality(cls, prompt_length: int, has_complex_math: bool) -> str: |
|
|
"""Recommend quality based on prompt complexity""" |
|
|
if prompt_length < 50 and not has_complex_math: |
|
|
return "medium_quality" |
|
|
elif prompt_length < 100: |
|
|
return "medium_quality" |
|
|
else: |
|
|
return "low_quality" |
|
|
|
|
|
@classmethod |
|
|
def optimize_code_for_performance(cls, code: str) -> str: |
|
|
"""Optimize Manim code for better performance""" |
|
|
optimized_code = code |
|
|
|
|
|
|
|
|
optimized_code = re.sub(r'self\.wait\((\d+)\)', |
|
|
lambda m: f'self.wait({min(int(m.group(1)), 3)})', |
|
|
optimized_code) |
|
|
|
|
|
|
|
|
optimized_code = re.sub(r'run_time=(\d+)', |
|
|
lambda m: f'run_time={min(int(m.group(1)), 5)}', |
|
|
optimized_code) |
|
|
|
|
|
|
|
|
performance_hints = [ |
|
|
"# Performance optimized: Limited wait times to 3 seconds max", |
|
|
"# Performance optimized: Limited animation run times to 5 seconds max" |
|
|
] |
|
|
|
|
|
if any(hint.split(": ")[1] in optimized_code for hint in performance_hints): |
|
|
optimized_code = "\n".join(performance_hints) + "\n" + optimized_code |
|
|
|
|
|
return optimized_code |
|
|
|
|
|
@classmethod |
|
|
def get_memory_efficient_settings(cls) -> Dict[str, str]: |
|
|
"""Get settings for memory-efficient rendering""" |
|
|
return { |
|
|
"preview": True, |
|
|
"disable_caching": True, |
|
|
"low_quality": True, |
|
|
"save_last_frame": False, |
|
|
"write_to_movie": True |
|
|
} |
|
|
|
|
|
@classmethod |
|
|
def calculate_estimated_file_size(cls, quality: str, duration: int = 10) -> Tuple[float, str]: |
|
|
"""Calculate estimated output file size in MB""" |
|
|
|
|
|
bitrate_estimates = { |
|
|
"low_quality": 1.0, |
|
|
"medium_quality": 3.0, |
|
|
"high_quality": 8.0, |
|
|
"production_quality": 25.0 |
|
|
} |
|
|
|
|
|
bitrate = bitrate_estimates.get(quality, 3.0) |
|
|
size_mb = (bitrate * duration) / 60 |
|
|
|
|
|
if size_mb < 1: |
|
|
return size_mb * 1024, "KB" |
|
|
else: |
|
|
return size_mb, "MB" |
|
|
|
|
|
@classmethod |
|
|
def get_optimization_tips(cls, quality: str, complexity: str) -> list: |
|
|
"""Get optimization tips based on settings""" |
|
|
tips = [] |
|
|
|
|
|
if quality in ["high_quality", "production_quality"]: |
|
|
tips.append("Consider using medium quality for faster rendering during development") |
|
|
|
|
|
if complexity in ["complex", "very_complex"]: |
|
|
tips.append("Break complex animations into smaller scenes for easier debugging") |
|
|
tips.append("Use preview mode (-p flag) to quickly test animations") |
|
|
|
|
|
tips.append("Close other applications to free up system resources") |
|
|
tips.append("Ensure sufficient disk space for temporary files") |
|
|
|
|
|
return tips |
|
|
|
|
|
def optimize_quality(self, available_memory: float, estimated_render_time: float) -> str: |
|
|
if available_memory < self.PERFORMANCE_THRESHOLDS["memory_usage"]: |
|
|
return "low_quality" |
|
|
if estimated_render_time > self.PERFORMANCE_THRESHOLDS["render_time"]: |
|
|
return "low_quality" |
|
|
return "medium_quality" |
|
|
|
|
|
@staticmethod |
|
|
def estimate_render_time(quality_level: str) -> int: |
|
|
"""Estimate render time based on quality level""" |
|
|
return PerformanceOptimizer.QUALITY_SETTINGS[quality_level]["performance_threshold"]["render_time"] |
|
|
|
|
|
@staticmethod |
|
|
def get_quality_settings(quality_level: str) -> dict: |
|
|
"""Get quality settings for the specified level""" |
|
|
return PerformanceOptimizer.QUALITY_SETTINGS[quality_level] |
|
|
|
|
|
@staticmethod |
|
|
def optimize_for_performance(quality_level: str, scene_complexity: int) -> dict: |
|
|
"""Optimize settings based on quality level and scene complexity""" |
|
|
settings = PerformanceOptimizer.QUALITY_SETTINGS[quality_level].copy() |
|
|
|
|
|
|
|
|
if scene_complexity > settings["max_objects"]: |
|
|
settings["max_objects"] = min(scene_complexity, settings["max_objects"] * 2) |
|
|
|
|
|
return settings |