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:
@@ -1,123 +1,89 @@
|
||||
# Plan: Full UI/UX Cleanup & Simplification
|
||||
# Plan: Render Pipeline Performance Optimizations
|
||||
|
||||
## Context
|
||||
|
||||
The OutputTypeTable was refactored from cramped 18-column inline editing to an expandable form row — a dramatic UX improvement. The same pattern should be applied to all other admin tables that suffer from the same problem. Additionally, several pages have UX inconsistencies (mixed editing patterns, tiny inputs, confusing controls) that need cleanup.
|
||||
Analysis of render logs shows the first render of a complex 140-part bearing takes 181s, while subsequent renders take 20s (OptiX cache — already fixed). Further optimizations can reduce per-render time and increase throughput.
|
||||
|
||||
**Principle**: Tables are for **viewing** data. Editing happens in **expandable rows** or **modals** — never in cramped inline cells.
|
||||
Current baseline (2048x2048, 256 samples, Cycles GPU, OIDN denoiser):
|
||||
- GLB import: 7-11s
|
||||
- GPU render: 11-13s (warm cache)
|
||||
- Total: 20-22s per render
|
||||
|
||||
## Affected Files
|
||||
## Tasks (in order of impact)
|
||||
|
||||
| File | Change | Priority |
|
||||
|------|--------|----------|
|
||||
| `frontend/src/components/admin/RenderTemplateTable.tsx` | Expandable edit row (11 cramped columns) | HIGH |
|
||||
| `frontend/src/components/admin/PricingTierTable.tsx` | Expandable edit row (6 columns, mixed add/edit) | HIGH |
|
||||
| `frontend/src/components/admin/GlobalRenderPositionsPanel.tsx` | Expandable edit row (8 columns, tiny inputs) | MEDIUM |
|
||||
| `frontend/src/pages/WorkerManagement.tsx` | Larger touch targets, better scale controls | MEDIUM |
|
||||
| `frontend/src/pages/Billing.tsx` | Fix status dropdown disguised as badge | MEDIUM |
|
||||
| `frontend/src/pages/OrderDetail.tsx` | Cleaner line table, material override UX | LOW |
|
||||
### [x] Task 1: Resolution-aware sample count for thumbnails
|
||||
|
||||
## Tasks (in order)
|
||||
|
||||
### [x] Task 1: RenderTemplateTable — expandable edit row
|
||||
|
||||
- **File**: `frontend/src/components/admin/RenderTemplateTable.tsx`
|
||||
- **What**: Same pattern as OutputTypeTable refactor:
|
||||
1. Display row ALWAYS shows (no conditional switching)
|
||||
2. Edit form opens as a new `<tr>` below with `colSpan` spanning all columns
|
||||
3. Grid form inside with labeled fields, grouped logically:
|
||||
- Row 1: Name, Category, Output Types (multi-select)
|
||||
- Row 2: Collection name, Material Replace, Lighting Only, Shadow Catcher, Camera Orbit
|
||||
- Row 3: .blend File (upload/download/re-upload with proper spacing)
|
||||
- Row 4: Active toggle + Save/Cancel buttons
|
||||
4. "Add new" form also uses the expandable pattern (button at top opens a full-width form row)
|
||||
5. Use `React.Fragment` for dual-row rendering
|
||||
- **Acceptance gate**: Edit mode shows a well-organized form below the display row; .blend file upload has proper spacing; no horizontal overflow
|
||||
- **File**: `backend/app/domains/pipeline/tasks/render_order_line.py`
|
||||
- **What**: When the output type resolution is <= 1024x1024 (thumbnails, previews), auto-scale samples down. Formula: `samples = max(32, base_samples * min(width, height) / 2048)`. Only apply when the output type doesn't explicitly set samples.
|
||||
- **Also**: `backend/app/domains/pipeline/tasks/render_thumbnail.py` — thumbnail renders use hardcoded settings; ensure they use low samples (32-64).
|
||||
- **Acceptance gate**: A 512x512 thumbnail uses ~64 samples instead of 256; a 2048x2048 HQ render still uses 256.
|
||||
- **Dependencies**: None
|
||||
- **Risk**: The .blend file upload flow (upload vs clone) is complex — needs careful preservation
|
||||
- **Risk**: Low — only affects auto-calculated samples, explicit per-OT samples override this
|
||||
- **Savings**: 50-75% GPU time on thumbnail/preview renders
|
||||
|
||||
### [x] Task 2: PricingTierTable — expandable edit row
|
||||
### [ ] Task 2: Prefer USD path over GLB when USD master exists
|
||||
|
||||
- **File**: `frontend/src/components/admin/PricingTierTable.tsx`
|
||||
- **What**: Replace inline cell editing with expandable form row:
|
||||
1. Display row always visible with read-only values
|
||||
2. Edit form as expandable row below with grid layout:
|
||||
- Row 1: Category Key (select), Quality Level (input), Price per Item (number input)
|
||||
- Row 2: Description (textarea, full width)
|
||||
- Row 3: Active toggle + Save/Cancel
|
||||
3. "Add Tier" button opens the same expandable form at the top
|
||||
4. Consistent visual: accent left border on edit row
|
||||
- **Acceptance gate**: No inline cell editing; form has proper labels; description gets full width
|
||||
- **File**: `backend/app/domains/pipeline/tasks/render_order_line.py`
|
||||
- **What**: The render task already checks for USD masters (lines 145-166) but the GLB tessellation step still runs as fallback. Audit the USD detection logic and ensure:
|
||||
1. When `usd_render_path` is found, skip GLB tessellation entirely (no `export_step_to_gltf` subprocess)
|
||||
2. Log when USD path is used vs GLB fallback
|
||||
3. The USD path should be the default when available
|
||||
- **Also check**: `backend/app/services/render_blender.py` — verify `render_still()` skips GLB conversion when `usd_path` is provided (line 100-101 says it does)
|
||||
- **Acceptance gate**: A product with a USD master renders without the 7-11s GLB tessellation step
|
||||
- **Dependencies**: None
|
||||
- **Risk**: Low — simple table with few fields
|
||||
- **Risk**: Low — USD path already works; this just ensures it's always preferred
|
||||
|
||||
### [x] Task 3: GlobalRenderPositionsPanel — expandable edit row
|
||||
### [ ] Task 3: Enable Blender persistent data for animations
|
||||
|
||||
- **File**: `frontend/src/components/admin/GlobalRenderPositionsPanel.tsx`
|
||||
- **What**: Replace inline cell editing with expandable form row:
|
||||
1. Display row keeps compact view (Name, X°, Y°, Z°, Focal, Default, Order)
|
||||
2. Edit form as expandable row:
|
||||
- Row 1: Name (wide), Is Default (checkbox with label)
|
||||
- Row 2: Rotation X°, Rotation Y°, Rotation Z° (number inputs with proper width)
|
||||
- Row 3: Focal Length mm, Sensor Width mm, Sort Order
|
||||
- Row 4: Save/Cancel buttons
|
||||
3. "Add Position" opens same expandable form
|
||||
4. Wider number inputs (`w-24` instead of `w-16`) for comfortable editing
|
||||
- **Acceptance gate**: Rotation inputs are comfortable to edit; no cramped cells; proper labels
|
||||
- **File**: `render-worker/scripts/turntable_render.py`
|
||||
- **What**: Add `scene.render.use_persistent_data = True` before rendering turntable frames. This keeps the BVH acceleration structure in memory between frames, avoiding rebuild for each of the 12-24 frames.
|
||||
- **Acceptance gate**: Turntable renders of complex products are 20-30% faster
|
||||
- **Dependencies**: None
|
||||
- **Risk**: Low — straightforward table
|
||||
- **Risk**: Low — Blender 5.0 supports this; increases VRAM usage slightly
|
||||
|
||||
### [x] Task 4: WorkerManagement — better scale controls
|
||||
### [ ] Task 4: Dual render queue for light/heavy workloads
|
||||
|
||||
- **File**: `frontend/src/pages/WorkerManagement.tsx`
|
||||
- **What**: Improve the concurrency/scale controls:
|
||||
1. Replace tiny `w-6` number displays with `w-12` minimum
|
||||
2. Make up/down buttons larger (`p-2` instead of default, `rounded-lg`)
|
||||
3. Add proper labels above each control ("Min Concurrency", "Max Concurrency")
|
||||
4. Group the scale controls in a card with clear section header
|
||||
5. Make the "Save" button more prominent (full-width at bottom of card)
|
||||
- **Acceptance gate**: Controls are easy to click; labels are clear; no overflow on mobile
|
||||
- **Files**:
|
||||
- `docker-compose.yml` — add second render-worker service for light tasks
|
||||
- `backend/app/domains/pipeline/tasks/render_thumbnail.py` — route thumbnails to light queue
|
||||
- `backend/app/domains/pipeline/tasks/render_order_line.py` — route based on resolution
|
||||
- **What**: Split `asset_pipeline` into two queues:
|
||||
- `asset_pipeline` — heavy renders (2048x2048, turntables): concurrency=1
|
||||
- `asset_pipeline_light` — thumbnails and small stills (<=1024): concurrency=2
|
||||
- Route based on output resolution or task type
|
||||
- **Acceptance gate**: Thumbnail generation doesn't block HQ renders; 2 thumbnails render concurrently
|
||||
- **Dependencies**: Task 1 (lower samples for light queue makes concurrent rendering safer)
|
||||
- **Risk**: Medium — VRAM contention if both workers render simultaneously. Mitigated by thumbnails being small (512x512, 64 samples = minimal VRAM)
|
||||
|
||||
### [ ] Task 5: Skip re-tessellation when GLB already exists
|
||||
|
||||
- **File**: `backend/app/services/render_blender.py`
|
||||
- **What**: In `render_still()`, the STEP→GLB tessellation runs every time. Cache the GLB file per CAD file (already stored as `gltf_geometry` MediaAsset). Before tessellating, check if a GLB MediaAsset exists for this cad_file_id and reuse it.
|
||||
- **Also**: `backend/app/domains/pipeline/tasks/render_order_line.py` — pass the existing GLB path to the render service when available
|
||||
- **Acceptance gate**: Second render of same product skips the 7-11s tessellation step; GLB is reused from MediaAsset
|
||||
- **Dependencies**: Task 2 (USD path is preferred; this is fallback for products without USD)
|
||||
- **Risk**: Low — GLB is deterministic per CAD file; if the CAD file changes, a new GLB is generated
|
||||
|
||||
### [ ] Task 6: Output format optimization (WebP for stills)
|
||||
|
||||
- **File**: `render-worker/scripts/_blender_scene_setup.py` (or `blender_render.py`)
|
||||
- **What**: After Blender renders a PNG, optionally convert to WebP for 50-70% smaller files. Add a `webp` output format option to OutputType. When selected, render as PNG then convert via Pillow.
|
||||
- **Also**: `backend/app/services/render_blender.py` — add post-render WebP conversion
|
||||
- **Acceptance gate**: WebP output type produces smaller files with no visible quality loss
|
||||
- **Dependencies**: None
|
||||
- **Risk**: Low — cosmetic changes only
|
||||
|
||||
### [x] Task 5: Billing — fix status dropdown UX
|
||||
|
||||
- **File**: `frontend/src/pages/Billing.tsx`
|
||||
- **What**: Fix the status dropdown that looks like a badge:
|
||||
1. Replace the `<select>` styled as a badge with an explicit dropdown button that opens a small popover/menu
|
||||
2. OR: keep the select but add a visible dropdown arrow indicator and border on hover to signal interactivity
|
||||
3. Make action buttons (download, delete) slightly larger with visible borders
|
||||
4. Add tooltips to icon-only action buttons
|
||||
- **Acceptance gate**: Users can tell the status is interactive (not just a badge); action buttons are easy to click
|
||||
- **Dependencies**: None
|
||||
- **Risk**: Low — UI-only changes
|
||||
|
||||
### [x] Task 6: OrderDetail — cleaner material override UX
|
||||
|
||||
- **File**: `frontend/src/pages/OrderDetail.tsx`
|
||||
- **What**: Polish the per-line material override dropdown:
|
||||
1. Move the material override dropdown from inside the "Output Type" column to its own column or a cleaner inline position
|
||||
2. Only show the dropdown on hover or when the line has an override set (reduce visual noise)
|
||||
3. The batch override dropdown above the table should be more visually prominent — card-style with label, not just inline text + select
|
||||
- **Acceptance gate**: Material override is discoverable but not visually noisy; batch override is clearly labeled
|
||||
- **Dependencies**: None
|
||||
- **Risk**: Low — visual adjustment
|
||||
- **Risk**: Low — WebP is widely supported; PNG is kept as default
|
||||
|
||||
## Migration Check
|
||||
|
||||
**No** — all changes are frontend-only.
|
||||
**No** — no database changes needed. All optimizations are in the render pipeline and Docker config.
|
||||
|
||||
## Order Recommendation
|
||||
|
||||
Tasks 1-3 can be done in parallel (independent admin components).
|
||||
Tasks 4-6 can be done in parallel (independent pages).
|
||||
1. Task 1 (sample scaling) — simple, immediate impact
|
||||
2. Task 2 (USD preference) — audit + small code change
|
||||
3. Task 3 (persistent data) — one-liner in turntable script
|
||||
4. Task 5 (GLB caching) — avoids redundant tessellation
|
||||
5. Task 4 (dual queue) — architecture change, needs testing
|
||||
6. Task 6 (WebP) — new feature, lowest priority
|
||||
|
||||
Recommended execution: Tasks 1-3 first (highest impact), then 4-6.
|
||||
|
||||
## Risks / Open Questions
|
||||
|
||||
1. **RenderTemplateTable .blend upload**: The file upload flow (choose file → upload → or clone from existing) is complex. The expandable form needs to preserve this exact functionality without breaking the upload mutation.
|
||||
|
||||
2. **Consistency with OutputTypeTable**: The expandable form pattern should look identical across all admin tables — same grid spacing, same accent border, same Save/Cancel button placement. Consider extracting a shared `ExpandableEditRow` wrapper if patterns diverge.
|
||||
|
||||
3. **Add-new forms**: Some tables have "Add" as a button that opens a modal-like form at the top, others have an inline row. Standardize: "Add" button at the top → opens expandable form row at the top of the table body (same pattern as edit).
|
||||
Tasks 1-3 can be done in parallel (independent files).
|
||||
|
||||
Reference in New Issue
Block a user