← Back to project

Unified geo-tex DiT — why the 60K checkpoint underperforms

full audit vs the released TRELLIS.2-4B · 2026-08-17
3d-genauditevalgeotex

TL;DR

The run did not train the config we thought it did. The launch script was edited 26 hours into a 30.5-hour run that never restarted. Six hyperparameters differ from what we believed was running.
Root cause found: our geometry stream barely reads the input image. Swap in a completely unrelated photo and the geometry output changes by 3%. For TRELLIS.2 the same swap changes it by 40%. Our geometry is mostly reproducing the ground-truth voxel occupancy we hand it for free.
Every headline metric we started with was misleading. Latent MSE ranked the visually-worst asset as the best of 16. Render PSNR said we beat TRELLIS.2 — because our voxels are GT-aligned by construction.
Not a wiring bug. Seven specific bug hypotheses were tested and ruled out with measurements. One structural suspect (RoPE applied one-sidedly between voxels and conditioning) remains untested and needs an A/B run.

1 · The run trained the wrong configuration

parameterwhat actually ranwhat we believed
mlp_ratio4.05.3334 (the released value)
p_corner0.40.2
p_lag0.0 — the lag regime was never trained0.4
dino_drop_prob0.3 — only 63% of samples had full conditioning0.0
EMA decay0.998 (~500-step window)0.9999
LR schedulelinear decay to 0constant with warmup

2 · Every metric we started with was lying

Latent MSE is diluted by easy voxels: the asset it ranks best is the one that looks worst.

2b · The other three failures

metrichow it liedevidence
latent MSErewards mean collapse on the tower we "beat" TRELLIS.2 1.294 vs 1.380 — with colour spread 77% of GT vs their 103%. We won by not committing.
render PSNRdominated by alignment we "win" 23.4 vs 21.9 purely because our voxel coordinates are the ground truth's and render pixel-aligned. TRELLIS.2 generates its own and gets punished for any shift (its pagoda scores 15.0 while looking fine).
signed detail ratio>100% is not better our baseball surface carries 181% of GT high-frequency — that is noise, not detail. Both directions are failures, so the metric must be |x/x_GT − 1|.
One common cause: any scalar averaged over all voxels or pixels is fooled by a distribution of "mostly trivial + a few decisive".

3 · Root cause — the geometry stream barely uses the image

Our texture stream reads the image (1.35). Our geometry stream does not (1.045). TRELLIS.2's does (1.40).

3b · Why it never had to learn

oursTRELLIS.2 img2shape
total steps60,0001,000,000 (from their released training config)
x single-image task50% (30% multi-view, 20% text)100%
x geometry has its own loss60% (masked at the t=0 corner)100%
x full conditioning63% (dropout 0.1 plus a second DINO dropout 0.3)90%
effective steps11,340900,000

4 · Consequence — CFG amplifies a near-zero direction

Monotonic across all five settings. At 7.5 the surface carries 181% of GT high-frequency; at 1.0 it carries 73%. Texture MSE improves 19% just by turning guidance down.

5 · What the failure actually looks like

Left GT, middle ours, right TRELLIS.2. Ours: ripples across the smooth ball, lattice mushed on the tower, thin slats dissolved on the bench.

6 · The cross test that isolated it

Our texture is fine on GT geometry AND on TRELLIS.2's geometry. It only breaks on our own.

7 · Measured against TRELLIS.2, on one common ruler

Geometry 2.9x, texture 3.0x. The '14%' was an artifact of the metric, not a real difference between the streams.

8 · Bug hypotheses tested and ruled out

hypothesishow it was ruled out
the geometry condition is wired wrong into the texture branch the same function produces crisp results on GT geometry and on TRELLIS.2's geometry, and fails only on ours — the only variable is the latent
the geometry loss weight is zero it is 1.0 and applied: total loss 1.003 = geo 0.406 + tex 0.597
resampling the texture noise every rollout step injects a random walk fixing the noise changed nothing: 112% vs 112% surface high-frequency, per asset
our timestep distribution differs from the official one both are uniform for the SLAT flows (only their sparse-structure stage is logit-normal)
joint attention masks geometry away from the conditioning one varlen flash attention over all three streams, no mask
train/inference conditioning assembly differs identical on the single-image path (the view-embedding add is multi-image only)
we run at a different voxel resolution than the official pipeline both 32³; their 512 pipeline uses ss_res=32 as well
the sampler parameters drifted all 10 fields match the released pipeline.json exactly

9 · Still open

Undertrained, or structurally unable to learn? Unresolved. Conditioning sensitivity was flat from step 52k to 60k (1.047 → 1.045) — but the LR had already decayed to ~0 there, so that proves nothing, and SAVE_KEEP=5 had deleted every earlier checkpoint. This determines whether more steps help at all.
RoPE is applied one-sidedly between voxels and conditioning. Conditioning tokens sit at coordinate (0,0,0) while voxel queries are rotated by their own 3D position, so voxel-to-conditioning attention is entangled with absolute position. TRELLIS.2 uses cross-attention, where neither side is rotated. FLUX-style MMDiT does zero its text ids, so the pattern exists in production — but text-to-image never needs precise patch-to-voxel correspondence and our geometry does. Untested: the objects fill the 32³ grid, leaving no room for the translation probe. It needs an A/B run.
Does the conditioning stream earn its 133M parameters? That is 20% of the model. TRELLIS.2 spends essentially nothing on conditioning (K/V projections inside cross-attention). The A/B this was supposed to win was never run.

10 · Next steps

#actionwhycost
1Run the aligned config for real; verify from checkpoint-2000, not from the script §1free
2Log conditioning sensitivity every 2000 steps answers §9 by step ~6000 instead of never — flat at 1.0 means structural, climbing means training length 2 forward passes
3Keep sparse early checkpoints (2k/6k/10k/20k)the trend was unrecoverable this rounddisk
4dino_drop 0.3 → 0removes the state that taught the model to work without DINOfree
5p_corner 0.4 → 0.2recovers 20% of geometry supervisionfree
6Recalibrate CFG on the val splitinference-only, measured -19% texture MSE20 min
7RoPE control armthe only way to settle §9's second itemone node
Recommendation: run a 5–10k step probe before committing another 30 hours. The probe is not for quality numbers — it answers one falsifiable question: with the config fixed, does conditioning sensitivity move off 1.045? If yes, it is a training-length problem and the full run is worth it. If no, it is structural and the RoPE arm becomes the priority instead of burning another 1,000 GPU-hours.
Calibrate expectations. We are 655M against three dedicated 1.29B specialists, and 60k steps against 1M. Even with every fix above, matching TRELLIS.2 is not the bar. Success for the next run is conditioning sensitivity moves and surface deviation drops from 0.261.

Appendix · measurement hygiene

655M three-stream MMDiT · 60,000 steps · 32xH200 · 30.5h · all numbers measured on checkpoint-60000, n=8-16 unless stated