perf: render pipeline optimizations — sample scaling, USD logging, persistent BVH
Task 1: Resolution-aware sample count - Auto-scale samples for resolutions <= 1024: max(32, samples * max_dim / 2048) - 512x512 thumbnails: 256 → 64 samples (75% GPU savings) - Thumbnail tasks capped at 64 samples via context manager - 2048x2048 HQ renders unchanged Task 2: USD path preference audit + logging - Verified USD master path is correctly preferred over GLB tessellation - Added clear emit() messages: "Using USD master" vs "No USD master — GLB path" - Dynamic render log label: "USD → Blender" vs "STEP → GLB → Blender" Task 3: Persistent BVH for turntable animations - Added scene.render.use_persistent_data = True before frame loop - BVH acceleration structure cached between frames (not rebuilt per frame) - Applies to both camera orbit and object rotation modes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,40 @@ from app.core.pipeline_logger import PipelineLogger
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Maximum samples for thumbnail renders (512x512).
|
||||
# Full-resolution renders use 256+ samples; thumbnails don't need more than 64.
|
||||
_THUMBNAIL_SAMPLE_CAP = 64
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _capped_thumbnail_samples():
|
||||
"""Temporarily cap render samples for thumbnail renders.
|
||||
|
||||
Thumbnails are 512x512 — using 256 Cycles samples is wasteful.
|
||||
This patches _get_all_settings in step_processor to cap samples
|
||||
at _THUMBNAIL_SAMPLE_CAP for the duration of the thumbnail render.
|
||||
"""
|
||||
import app.services.step_processor as _sp
|
||||
_original = _sp._get_all_settings
|
||||
|
||||
def _patched() -> dict[str, str]:
|
||||
settings = _original()
|
||||
for key in ("blender_cycles_samples", "blender_eevee_samples"):
|
||||
try:
|
||||
val = int(settings.get(key, "256"))
|
||||
if val > _THUMBNAIL_SAMPLE_CAP:
|
||||
logger.info("Capping thumbnail %s: %d -> %d", key, val, _THUMBNAIL_SAMPLE_CAP)
|
||||
settings[key] = str(_THUMBNAIL_SAMPLE_CAP)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
return settings
|
||||
|
||||
_sp._get_all_settings = _patched
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
_sp._get_all_settings = _original
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _pipeline_session(tenant_id: str | None = None):
|
||||
@@ -67,11 +101,12 @@ def render_step_thumbnail(self, cad_file_id: str):
|
||||
except Exception:
|
||||
logger.warning(f"step_file_hash computation failed for {cad_file_id} (non-fatal)")
|
||||
|
||||
# ── Render thumbnail ──────────────────────────────────────────────────
|
||||
# ── Render thumbnail (with capped samples for 512x512) ──────────────
|
||||
try:
|
||||
from app.services.step_processor import regenerate_cad_thumbnail
|
||||
pl.info("render_step_thumbnail", "Calling regenerate_cad_thumbnail")
|
||||
success = regenerate_cad_thumbnail(cad_file_id, part_colors={})
|
||||
with _capped_thumbnail_samples():
|
||||
success = regenerate_cad_thumbnail(cad_file_id, part_colors={})
|
||||
if not success:
|
||||
raise RuntimeError("regenerate_cad_thumbnail returned False")
|
||||
except Exception as exc:
|
||||
@@ -166,7 +201,8 @@ def regenerate_thumbnail(self, cad_file_id: str, part_colors: dict):
|
||||
|
||||
try:
|
||||
from app.services.step_processor import regenerate_cad_thumbnail
|
||||
success = regenerate_cad_thumbnail(cad_file_id, part_colors)
|
||||
with _capped_thumbnail_samples():
|
||||
success = regenerate_cad_thumbnail(cad_file_id, part_colors)
|
||||
if not success:
|
||||
raise RuntimeError("regenerate_cad_thumbnail returned False")
|
||||
except Exception as exc:
|
||||
|
||||
Reference in New Issue
Block a user