feat(trellis_mac): close the fal quality gap — RAM-sized bake cap + forced-opaque alpha

Local GLBs were crushed to exactly 94,535 faces (fal ships ~495k) by the
200k laptop mtlbvh guard + o_voxel cleanup, and noisy baked alpha flipped
materials to BLEND (speckle-veil hair, 'broken face'). Neither was the
diffusion — local decode was already 2.2M tris.

- run.py: default TRELLIS2_MAX_BAKE_FACES by node RAM (>=96GB -> 500k,
  else 200k laptop-safe); pass max_bake_faces/alpha_mode/steps/baker
  params through (server forwards only client-sent params — manifest
  defaults are UI-only)
- manifest: new params, quality defaults
- BENCHMARKS.md: 2026-07-22 entry — 500k cap verified crash-free on M3
  Ultra, 351,898-face GLB, bake 7s->14s, farm-path e2e verified

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-22 19:04:23 +10:00
parent 61c63e513e
commit b1d37b1629
3 changed files with 50 additions and 1 deletions

View File

@ -448,3 +448,30 @@ monster/trellis-2-mrp-mlx to ~/trellis2-mlx + scripts/setup_macos.sh
(no HF login needed on workers). Both Ultras now serve the fastest
local TRELLIS.2 in existence. TRELLIS.2 fleet: m3 primary (72s),
m1 second lane (182s), zero dollars per asset.
## 2026-07-22 — trellis_mac closes the fal quality gap (m3ultra, black-model image, seed 0, pipeline 1024)
Root-caused why local GLBs looked far worse than fal's TRELLIS.2 despite the
same 4B weights. Two independent bugs, neither in the diffusion (local decode
was already 2.23M tris / 101s):
1. **Decimation ceiling.** generate.py's `TRELLIS2_MAX_BAKE_FACES=200000`
laptop mtlbvh guard + o_voxel's cleanup pass (dup/degenerate removal +
small-component pruning) crushed every model to exactly **94,535 faces**
(fal ships ~495k). The 23× pre-bake simplify also shredded thin shells —
hair fragments then got deleted by `remove_small_connected_components`.
Fix: cap raised to **500k → 351,898-face GLB**, bake 7s→14s, no Metal
watchdog crash on the M3 Ultra Studio. Now the operator default
(`max_bake_faces` param; drop to 200k on laptops).
2. **Speckle-veil alpha.** o_voxel's Metal baker samples the decoder's alpha
attr noisily (same family as the 07-19 dark-patch bug); any texel <250
flips the material to BLEND → solid hair renders as a transparent speckle
veil, "broken face". Fix: generate.py now rewrites the exported GLB to
`alphaMode=OPAQUE` by default (`TRELLIS2_ALPHA_MODE=auto` keeps baked
alpha for glassy subjects; `alpha_mode` operator param).
Verified in Blender side-by-side (old 95k / new 352k opaque / fal 495k):
face + hair now read at fal level. Remaining deltas vs fal: slightly fuller
fal hair (try `max_bake_faces=1000000`), and the known Metal dark-patch
texels (workaround: `baker=vertex`). Also wired `steps` param passthrough.

View File

@ -14,7 +14,11 @@
"pipeline_type": {"type": "string", "enum": ["512", "1024", "1024_cascade"], "default": "1024", "description": "Resolution tier (1024_cascade = highest quality on Mac)"},
"texture_size": {"type": "integer", "enum": [512, 1024, 2048], "default": 2048, "description": "Baked texture resolution"},
"seed": {"type": "integer", "default": 0, "description": "Random seed"},
"no_texture": {"type": "boolean", "default": false, "description": "Geometry only (skip texture stage, much faster)"}
"no_texture": {"type": "boolean", "default": false, "description": "Geometry only (skip texture stage, much faster)"},
"max_bake_faces": {"type": "integer", "enum": [200000, 500000, 1000000], "default": 500000, "description": "Pre-bake simplify cap. 500k ≈ fal parity, verified crash-free on the M3 Ultra (2026-07-22); drop to 200k on laptops (mtlbvh guard)"},
"steps": {"type": "integer", "default": 0, "description": "Sampler steps override for all 3 flow phases (0 = pipeline default, 12)"},
"baker": {"type": "string", "enum": ["", "vertex"], "default": "", "description": "'vertex' = KDTree vertex-color bake (dark-patch workaround, no PBR maps)"},
"alpha_mode": {"type": "string", "enum": ["opaque", "auto"], "default": "opaque", "description": "opaque = force alphaMode OPAQUE in the GLB (fixes speckle-veil hair from noisy baked alpha); auto = keep o_voxel's BLEND detection for glassy subjects"}
}
}
}

View File

@ -34,6 +34,8 @@ cmd = [
]
if p.get("no_texture"):
cmd.append("--no-texture")
if p.get("steps"):
cmd += ["--steps", str(p["steps"])]
env = os.environ.copy()
# xet chunked downloader fails on these BFL/Meta repos ("Unable to parse string
@ -43,6 +45,22 @@ env["HF_HUB_DISABLE_XET"] = "1"
env["KMP_DUPLICATE_LIB_OK"] = "TRUE"
env.setdefault("OMP_NUM_THREADS", "1")
env.setdefault("MKL_NUM_THREADS", "1")
# Pre-bake simplify cap. The 200k generate.py default is a laptop-derived
# mtlbvh guard that costs most of the mesh detail (2.2M-tri decode → 95k GLB
# vs fal's 495k). 500k verified crash-free on the M3 Ultra Studio 2026-07-22.
# The server passes only client-sent params (manifest defaults are UI-only),
# so default here, sized by node RAM: big-memory Studios get the quality cap.
if p.get("max_bake_faces") is not None:
env["TRELLIS2_MAX_BAKE_FACES"] = str(p["max_bake_faces"])
else:
mem_gb = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1e9
env.setdefault("TRELLIS2_MAX_BAKE_FACES", "500000" if mem_gb >= 96 else "200000")
if p.get("baker"):
env["TRELLIS2_BAKER"] = str(p["baker"])
# "auto" keeps o_voxel's baked-alpha BLEND detection (glass etc.); default
# opaque — the Metal baker's noisy alpha turns solid hair into speckles.
if p.get("alpha_mode"):
env["TRELLIS2_ALPHA_MODE"] = str(p["alpha_mode"])
print("+", " ".join(cmd), flush=True)
print("(first run downloads TRELLIS.2-4B + dinov3 + RMBG-2.0 weights)", flush=True)