diff --git a/BENCHMARKS.md b/BENCHMARKS.md index fe7aa9e..2349b2e 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -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. diff --git a/server/operators/trellis_mac/manifest.json b/server/operators/trellis_mac/manifest.json index e97356a..99ed0bc 100644 --- a/server/operators/trellis_mac/manifest.json +++ b/server/operators/trellis_mac/manifest.json @@ -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"} } } } diff --git a/server/operators/trellis_mac/run.py b/server/operators/trellis_mac/run.py index bb2b8ba..7b083f4 100644 --- a/server/operators/trellis_mac/run.py +++ b/server/operators/trellis_mac/run.py @@ -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)