Releases: dreamrec/ComfyUI-Pixal3D
Release list
v0.1.10 — 1536_cascade unlocked + save_obj side-export
🎯 1536_cascade actually fits on a 34 GB GPU
Empirical finding from 20 mesh-generation runs on RTX 5090: 1536_cascade works in standalone mode as long as low_vram=True is on. The README's previous claim that 1536 OOMs on ≤34 GB cards was only true under eager device placement (low_vram=False); with low_vram=True, upstream Pixal3D offloads the 4× DinoV3 image_cond_model extractors to CPU between stages, which frees enough headroom for the shape-1536 NAF attention to land.
Verified on RTX 5090: 3 back-to-back successful runs with 1536_cascade + low_vram=True + max_num_tokens=49152 + texture_size=4096 + 16/16/16 steps + 300k decimation — every run completed in 64-71 seconds warm, peak allocated ~32 GB.
Bundled workflow changes
Both demo workflows now ship the verified-good 1536 recipe:
| Widget | Was (v0.1.9) | Now (v0.1.10) |
|---|---|---|
pipeline_type |
1024_cascade |
1536_cascade |
max_num_tokens |
32768 |
49152 |
save_obj |
(new widget) | True |
low_vram |
true |
true (unchanged) |
New: save_obj widget on Pixal3DImageToMesh
When True alongside save_to_output_dir=True, after writing the GLB the node calls glb.export() again with .obj extension. trimesh writes .obj + .mtl sidecar + a base-color PNG next to it.
OBJ is base-color-only (the format has no standard metallic/roughness slots) — keep the GLB as the canonical PBR artifact and use the OBJ for DCC tools that prefer it (Blender, ZBrush, Marmoset, older pipelines).
The widget is appended at the end of the node's optional inputs so old workflow JSONs keep parsing — their widgets_values lists just default to False until updated.
README rewrites
- Memory + performance table promotes
1536_cascadeas the top row with verified-on-5090 numbers (~32 GB peak, ~65-70 s warm). - New "1536_cascade unlock" subsection explains why
low_vram=Trueis the key, with the 3-runs-in-a-row evidence. - Fragmentation footnote:
1024_cascade + low_vram=False + 49k tokenssucceeded 10 times then OOMed on the 11th in the same session. If you hit this, queuePixal3D: Free Pipelinebetween batches or restart ComfyUI. - Upstream roadmap rephrased: visualbruno's
use_tiled_decoderwould unlock 1536 on 24 GB cards (we already fit it on 34 GB vialow_vram=True).
Full Changelog: v0.1.9...v0.1.10
v0.1.9 — safer bundled demo defaults for standalone mode
Why this exists
v0.1.7+ runs Pixal3D in ComfyUI Desktop's .venv (standalone mode) where
comfy-aimdo is loaded and pre-allocates a 16 GB cudaMallocAsync cast
buffer on top of Pixal3D's own ~14 GB of weights + activations. The old
v0.1.3 demo defaults (16/16/16 steps, max_num_tokens=65536,
low_vram=False) were tuned for pozzettiandrea's TRELLIS2 worker
subprocess where mmgp is NOT loaded — peak ~14 GB. In standalone mode
the same workflow OOMs at ~29-30 GB on a 5090 (34 GB ceiling), specifically
in get_proj_cond_shape's NAF attention at shape-1024 resolution.
What changed
Bundled workflow widget defaults (both pixal3d_image_to_mesh.json and
pixal3d_image_to_mesh_with_external_rembg.json):
| Widget | Was | Now |
|---|---|---|
low_vram |
false |
true |
max_num_tokens |
65536 |
32768 |
These are the standalone-mode-safe settings that fit on 16-24 GB cards.
On 32 GB+ cards (RTX 5090, A6000-class), flip low_vram back to false
in the widget to cut wall time roughly in half.
The in-workflow Note node explains both the new defaults and the
flip-to-low_vram=false speed-up path.
README
The "Memory + performance" table is rewritten for standalone-mode
reality — all peak-VRAM numbers now reflect what users actually
measure. A new bullet calls out the comfy-aimdo cast-buffer tax
explicitly so the next Googler who hits "OOM on demo workflow"
lands directly on the cause.
What didn't change
No code changes — pyproject.toml version bump only. v0.1.8's runtime
device-placement fix + v0.1.7's mmgp / snapshot_download / install
fallback all carry forward.
Full Changelog: v0.1.8...v0.1.9
v0.1.8 — fix device-mismatch on cache-hit low_vram toggle
Fix: Input type (cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same
Running the bundled demo workflow (low_vram=False) after a prior queue run
that used low_vram=True would crash with the device-mismatch error above,
at image_conditioned_proj.py:511 (DinoV3 image extractor forward).
Root cause
load_pipeline's cache-hit fast path was just setting _pipeline.low_vram = low_vram
and returning. Three submodel groups end up scattered across devices after a
low_vram=True run, and none of them get re-moved on toggle:
self.models(SS / shape / tex flow + decoders) — upstreamPipeline.to()
iteratesself.models.values(), but only whenlow_vram=False.self.rembg_model— upstreampipeline.to()moves it when
low_vram=False; inlow_vram=Truemodepreprocess_image()does it lazily.self.image_cond_model_{ss, shape_512, shape_1024, tex_1024}(4x DinoV3
projection extractors) — these are not touched by upstreampipeline.to().
Eachget_proj_cond_*doesif self.low_vram: m.to(device); …; m.cpu()
after use, so a priorlow_vram=Truerun parks all four on CPU. A follow-up
low_vram=Falserun skips the lazy move and forward fails.
Fix
On cache hit, set self.low_vram first (so upstream's pipeline.to() picks the
correct branch), then call pipeline.to(device). When transitioning to
low_vram=False, additionally re-move the four image_cond_model_* extractors
to the device — matching what the initial-load path already does.
Verified
End-to-end with the actual toggle sequence:
- fresh load
low_vram=True→ 98s GLB - cache hit, toggle to
low_vram=False→ 63s GLB, no device error
Full Changelog: v0.1.7...v0.1.8
v0.1.7 — standalone load_pipeline fix
Fix: Standalone install actually works now
v0.1.6 shipped to the Comfy Registry but failed at first run with a misleading
Repository Not Found for url: https://huggingface.co/ckpts/... 404. v0.1.7
fixes the real root cause (three layers deep) and adds standalone-mode install support.
Root cause analysis
comfy-aimdo'smmgplib (shipped in ComfyUI Desktop's.venv)
monkey-patchessafetensors.torch.load_filewith a memory-mapped reader.
Its dtype map atmmgp/safetensors2.py:33omits complex dtypes; one
Pixal3D decoder checkpoint contains a singleC64(complex64) tensor →
KeyError: 'C64'blows up the load.- Upstream Pixal3D's
pipelines/base.py:43-46has a bareexcept Exception
that silently swallows thatKeyErrorand retries with the relative path. - The retry path in
models/__init__.py:60misparses relative paths like
"ckpts/ss_flow_..."as Hugging Face repo IDs and emits the bogus 404 we see.
Fixes (nodes/pixal3d_stages.py)
snapshot_download(model_path)resolves to an absolute local snapshot dir
beforefrom_pretrained, so the loader'sos.path.existscheck (relative
to CWD) always succeeds.- Pre-extend
mmgp.safetensors2._map_to_dtypewithC64/C128so the mmap
reader handles complex tensors. - Wrap
from_pretrainedintorch.inference_mode(False)as belt-and-suspenders
againstload_state_dictversion-counter errors on freshly-constructed
nn.Moduleparameters under ComfyUI's outerinference_mode.
Fixes (install.py)
- Standalone-mode fallback: if no
pozzettiandrea/ComfyUI-TRELLIS2
sibling exists, install deps intosys.executableinstead of hard-failing.
This matches how Comfy Registry / Manager invokeinstall.pywith the
.venv's python — fresh Registry installs now work out of the box. - Worker python search prefers
C:\ce\_env_*(the real comfy-env worker
pool) over any in-plugin stubScripts/python.exe. install_requirementssplitsrequirements.txtinto VCS / hashed / plain
buckets so pip's--require-hashesmode doesn't reject the VCS MoGe entry.- Error message:
visualbruno→pozzettiandrea(correct upstream fork).
Verified
End-to-end on ComfyUI Desktop + RTX 5090: 56.5s warm run with 8/8/8 steps +
1024 texture + low_vram=True → 8.6 MB GLB output.
Full Changelog: v0.1.6...v0.1.7
v0.1.6 — docs: correct upstream TRELLIS2 fork attribution
Documentation-only fix. No code, workflow, or demo image changes.
The bug
The README and NOTICE.md were pointing at `visualbruno/ComfyUI-Trellis2` as our TRELLIS2 upstream. That's wrong — the wrapper that actually gets installed via ComfyUI Manager (and that this plugin's `install.py` looks for under `custom_nodes/ComfyUI-TRELLIS2/`) is pozzettiandrea/ComfyUI-TRELLIS2.
The two forks share the Microsoft TRELLIS.2 lineage but have different `init.py` structure, different node organization, and divergent commit histories. The mistaken attribution was caught when investigating which fork "the new TRELLIS2 commits" actually came from — the deployed install matched pozzettiandrea byte-for-byte; visualbruno's pixal3d-branch commits are unrelated to what users actually have.
What changed
- README.md: 5 install/requirements/credits references updated to pozzettiandrea. One remaining mention of visualbruno is kept intentionally in the Upstream Roadmap section as a pointer to their experimental pixal3d branch (`use_tiled_decoder`, etc.) — labeled as a separate experimental fork, not as our actual upstream.
- NOTICE.md: third-party-licenses table updated.
What didn't change
No code, no workflows, no demo images. Just the docs.
Full diff: v0.1.5...v0.1.6
v0.1.5 — STRING output path + custom save prefix + optional auto-save
Three additive widget upgrades on `Pixal3DImageToMesh`. Purely wrapper-layer; the underlying `pipeline.run()` integration is byte-identical to v0.1.4 and remains live-validated. Defaults preserve existing behavior.
New widgets
- `save_to_output_dir` (BOOLEAN, default `True`) — when `False`, skip the auto-save of the GLB to `ComfyUI/output/`. Useful for chained workflows that export downstream via `Trellis2ExportTrimesh` / `SaveGLB`.
- `output_filename_prefix` (STRING, default `"pixal3d"`) — customize the saved GLB's filename prefix. Final name: `<unix_ns>.glb`. Sanitized to ASCII alnum + `_-` so no path traversal.
- `keep_warm` (from v0.1.4) unchanged.
New output port
`RETURN_TYPES` extended 4-tuple → 5-tuple:
- `glb_path` (STRING) — absolute path of the saved GLB. Connect directly into `Preview3D`'s `model_file` slot to skip the explicit `Trellis2ExportTrimesh` hop. Empty string when `save_to_output_dir=False`.
Diagnostic logging
`Pixal3DLoadPipeline` now logs per-stage timing (pipeline load, MoGe load, total). When a cold-load takes 8+ minutes, users can see which phase is the bottleneck instead of staring at silence.
What we deliberately didn't add
Investigated and ruled out:
- `use_tiled_decoder` widget (TRELLIS2's 1536_cascade fix) — lives in their wrapper layer that calls Pixal3D sub-methods directly. Adopting it = rewriting our single-node design into a multi-node graph. See README's Upstream Roadmap.
- `pipeline_type` modes `"512"` and `"1024"` (non-cascade) — Tencent's `pipeline.run()` raises `ValueError("Invalid pipeline type for Pixal3D proj mode")` for those values. They only exist in TRELLIS2's per-stage architecture.
- Pixal3D-T model variant — separate Tencent model marked "not compatible with all nodes" upstream. Defer.
Backward compatibility
Every existing workflow JSON keeps loading. The new widgets have defaults that produce the v0.1.4 behavior. The new STRING return port is the 5th slot — existing workflows wired to ports 0-3 keep working unchanged.
Full diff: v0.1.4...v0.1.5
v0.1.4 — keep_warm widget + memory hygiene + upstream roadmap
Targeted refactor after auditing the visualbruno/ComfyUI-Trellis2 pixal3d branch recent commits. No node-API breakage — drop-in over v0.1.3.
What's new
keep_warm widget on Pixal3DImageToMesh
New BOOLEAN widget (default True). When False, the ~14 GB Pixal3D pipeline singleton is freed at the end of the run instead of being kept resident. Default preserves prior behavior (warm singleton → next call ~3 min; cold → ~7-10 min).
Use keep_warm=False when you have a single Pixal3D-then-other-stuff workflow and want the VRAM back without queuing a separate Pixal3D: Free Pipeline node.
Tighter memory hygiene in the post-sampling stages
run_pixal3d now drops latent / mesh references in the right order so peak VRAM during mesh extraction has the most possible headroom:
tex_slat(larger) is freed beforeshape_slatmesh_listis dropped after the single mesh is taken- raw mesh (vertices, coords, voxel attrs — ~1-2 GB on
1536_cascade) freed after GLB bake - explicit
gc.collect()+torch.cuda.empty_cache()between stages
No behavior change — just earlier release of references we no longer need.
README: upstream roadmap section
Documents the TRELLIS2 pixal3d branch in flight — use_tiled_decoder, expanded pipeline_type, per-stage memory management, standard natten 0.21.6 wheel — so anyone reading the docs knows what's coming and why this plugin stays on 1024_cascade defaults for now.
Memory + performance table refreshed
Bundled-workflow row added; 1536_cascade OOM ceiling explicitly called out (~30 GB Pixal3D models + ~16 GB comfy-aimdo cast buffer = 46 GB > 34 GB on a 5090).
What we deliberately didn't change
TRELLIS2's per-stage memory wins come from calling Pixal3D's pipeline sub-methods directly across separate ComfyUI nodes. Our plugin uses pipeline.run() monolithically — same model, same outputs, different control surface. The use_tiled_decoder knob also lives in TRELLIS2's wrapper layer, not in Tencent's Pixal3D pipeline itself. Both of those become accessible to us only when:
- Tencent's upstream
pixal3d/pipelines/pixal3d_image_to_3d.pyexposes the tiled-decoder hook, OR - We rewrite our
run_pixal3dto call Pixal3D's sub-methods (SS / shape / tex / decoders) directly, like TRELLIS2's wrapper does.
Option 2 is a significant rewrite and would couple us tightly to Pixal3D's internal API. Holding off until upstream lands either.
Full diff: v0.1.3...v0.1.4
v0.1.3 — workflow polish + demo refresh + rescue speedup
Workflow polish, fresh demo images, and a one-line speedup to the cold-load path. No node-API changes — drop-in replacement for v0.1.2.
Changes since v0.1.2
Code
nodes/pixal3d_stages.py—_rescue_inference_tensorsnow short-circuits when the caller has already disabledinference_mode.run_pixal3dwraps the whole pipeline call intorch.inference_mode(mode=False), so any tensor allocated inside is already a regular tensor and the rescue is a no-op — but iterating 4× DinoV3 ViT-L + Pixal3D parameters at Python loop speed was costing ~20 minutes single-threaded on cold-load. Now it returns immediately.
Workflows (workflows/pixal3d_image_to_mesh.json + _with_external_rembg.json)
- Interactive 3D viewer: added
Trellis2ExportTrimesh→Preview3Dchain so users see the textured mesh rotated in-canvas. The redundant 8-viewTrellis2RenderPreviewwas removed (Preview3D is more useful for inspection). - Three-lane grid layout: Inputs (LoadImage + optional rembg) → Pipeline (Pixal3DImageToMesh sized 380×950 so every widget is visible) → Outputs (PreviewImage + Save GLB + Preview3D), all on a clean integer grid. The viewport
extra.dsis pre-framed for 1080p. - In-canvas Note node with the verified low-VRAM tweak ladder for 12–16 GB cards.
- Higher-quality safe defaults:
1024_cascade+ 16/16/16 sampling steps + 65k tokens + 300k decimation + 4096 texture. ~3 min wall time when the pipeline is warm; ~7–10 min on the first run after a ComfyUI restart. Verified live on RTX 5090 — does NOT trip the comfy-aimdo 16 GB cast-buffer + 1536_cascade OOM ceiling.
Demo images (docs/images/)
demo_4view.png— 4 PBR-textured views (front / right-3/4 / back / left-3/4) of the new demo subject, rendered offline via pyrender at 2× SSAA with three-point lighting. Bypasses Trellis2RenderPreview's spike/aliasing artifacts.demo_mesh_frontal.png— clay-shaded frontal view of the same mesh so reviewers can read geometry quality independent of the PBR bake.- New demo subject (
examples/test_image.png) — significantly harder than the previous painted-miniature turtle; better showcase of what the pipeline does on intricate compositions.
README
- Hero block updated with both demo images + clarifying captions (textured + clay).
- VRAM-requirements line now points at the workflow's in-canvas Note for low-VRAM tuning.
Install paths
- ComfyUI Desktop (via Comfy Registry): in-app node browser → search "Pixal3D". The bumped pyproject.toml auto-triggers a fresh registry publish.
- ComfyUI Manager: search "Pixal3D".
- Manual:
cd ComfyUI/custom_nodes && git clone https://github.com/dreamrec/ComfyUI-Pixal3D && cd ComfyUI-Pixal3D && python install.py
Full diff: v0.1.2...v0.1.3
v0.1.2 — audit fixes: installer detection + packaging polish
Maintenance release driven by a full-repo audit. No node-behavior or model changes — the Pixal3D pipeline, Trellis2 dependency, and Windows + RTX 30/40/50 requirements are identical to v0.1.1.
Changes since v0.1.1
install.py— replaced fragile string-split TOML parsing withtomllib, and broadened worker-python detection so the installer now handles the newercomfy-env-managerlayouts:- in-plugin
<trellis2>/_env_*/Scripts/python.exe(Windows venv) <env_root>/env/python.exe(symlink to.pixi/envs/default/)- legacy
<env_root>/.pixi/envs/default/python.exe SystemExitnow enumerates every path it probed, so it's actually diagnostic when detection fails.
- in-plugin
pyproject.toml— addedrequires-python = ">=3.12"and theProgramming Language :: Python :: 3.12classifier so pip refuses incompatible interpreters up-front instead of failing at runtime with an opaque ABI mismatch.prestartup_script.py— clarifying note in the module docstring: theSubprocessWorkertimeout patch is defensive (the new in-process registration model means Pixal3D nodes don't go throughcomfy_env), kept as cheap insurance.nodes/nodes_pixal3d.py— removed deadimport osandimport folder_paths(folder_pathsis only used insidepixal3d_stages._run_pixal3d_body).- Docs —
docs/BUILD_NATTEN.mdandNOTICE.mdcorrected to reference the actual bundled wheel namenatten-0.21.0+winsm89ptx-cp312-cp312-win_amd64.whl(waswinsm89without theptxbuild tag). workflows/pixal3d_image_to_mesh_with_external_rembg.json— appended"gray"towidgets_valuesso it matches the current node schema. (The sibling workflow JSON already had it; this one was authored beforebackground_colorwas added.)
Install paths
- ComfyUI Desktop (via Comfy Registry): in-app node browser → search "Pixal3D".
- ComfyUI Manager: search "Pixal3D" once Comfy-Org/ComfyUI-Manager#2874 merges.
- Manual:
cd ComfyUI/custom_nodes && git clone https://github.com/dreamrec/ComfyUI-Pixal3D && cd ComfyUI-Pixal3D && python install.py
Full diff: v0.1.1...v0.1.2
v0.1.1 — Registry metadata + auto-publish
Metadata-only release that wires the project into the ComfyUI Registry publish flow and populates platform fields.
Changes since v0.1.0
pyproject.tomladded with[tool.comfy]block (PublisherId = "dreamrec",DisplayName = "ComfyUI-Pixal3D").- GitHub Action
publish-comfy-registry.ymlauto-publishes the node on everypyproject.tomlbump pushed tomain. - Platform classifiers added so the in-app installer can filter correctly:
Operating System :: Microsoft :: WindowsEnvironment :: GPU :: NVIDIA CUDA
- Banner wired to
docs/images/demo_4view.png(raw GitHub URL).
Install paths
- ComfyUI Desktop (via Comfy Registry): in-app node browser → search "Pixal3D".
- ComfyUI Manager (custom_nodes path): Comfy-Org/ComfyUI-Manager#2874 is open and awaiting maintainer merge.
- Manual:
cd ComfyUI/custom_nodes && git clone https://github.com/dreamrec/ComfyUI-Pixal3D
No node-behavior or model changes vs v0.1.0 — same Pixal3D pipeline, same Trellis2 dependency, same Windows + RTX 30/40/50 requirements.