→ https://gpu.local-xyz.ru/sharp/ — попробуй: загружай фото, жми «🏆 Canonical bake» — теперь возвращает твой painted .glb.
TASK-050 интегрировал per-user Hunyuan в /sharp/: instant 3 сек + 360° fusion 30 сек + canonical paint… который падал. Вместо real PBR-painted .glb пользователь получал mesh-only резервный вариант (per-user .glb без покраски). Сегодня закрыл.
Что сломалось
ComfyUI workflow node Hy3DRenderMultiView крашился с:
ModuleNotFoundError: No module named 'custom_rasterizer'
File "ComfyUI-Hunyuan3DWrapper/hy3dgen/texgen/differentiable_renderer/mesh_render.py", line 158
import custom_rasterizer as cr
Это deceptive error — import custom_rasterizer падает не потому что модуль reallly missing (он installed как editable в venv), а потому что зависимый kernel custom_rasterizer_kernel.cpython-...so падает при импорте, а Python показывает только верхний ModuleNotFoundError.
Где сидела ошибка
Custom_rasterizer Python package структура:
custom_rasterizer/
__init__.py ← импортирует .render
render.py ← `import custom_rasterizer_kernel` (compiled .so)
custom_rasterizer_kernel.cpython-312-x86_64-linux-gnu.so ← C++ extension
custom_rasterizer_kernel.so зависит от libc10.so (PyTorch native). Если torch ещё не loaded в process’е — dlopen не находит libc10 и падает. В ComfyUI worker’е import order был:
# render.py (broken):
import custom_rasterizer_kernel ← фейлится, libc10 не loaded
import torch
Fix №1 — torch preload
Поправил __init__.py загружать torch раньше:
"""TASK-051: torch preload before kernel import."""
import torch # preload libc10.so
from .render import *
Fix №2 — copy .so в local package
Этого было мало. ComfyUI добавляет в sys.path custom_nodes/ComfyUI-Hunyuan3DWrapper/hy3dgen/texgen/, и там есть локальная копия custom_rasterizer без compiled .so:
custom_rasterizer/ ← ComfyUI-local (no .so)
custom_rasterizer/ ← package
__init__.py
render.py ← imports kernel
build/lib.../*.so ← .so в build/ но не в package/
Скопировал working .so из ~/code/Hunyuan3D-2.1/hy3dpaint/custom_rasterizer/ в local ComfyUI dir:
SRC=.../custom_rasterizer_kernel.cpython-312-x86_64-linux-gnu.so
cp $SRC ~/comfy/.../custom_rasterizer/ # for sys.path
cp $SRC ~/comfy/.../custom_rasterizer/custom_rasterizer/ # for package
И обновил local __init__.py тем же torch-preload.
Fix №3 — ComfyUI restart
ComfyUI кэширует Python imports на process lifetime. Чтобы подхватить новый __init__.py:
tmux kill-session -t comfy
tmux new -d -s comfy "cd ~/comfy/ComfyUI && python main.py ..."
Cold ComfyUI restart — ~30 секунд до first request ready.
Результат
$ time python /tmp/hy3d_meshgen.py alpha-ref.png /tmp/test_paint.glb --paint
[hy3d] workflow=/tmp/hy3d_pbr.json in=alpha-ref.png → out=test_paint.glb
[hy3d] queued prompt_id=77e2858c-91cf-4140-a5ee-89ce5ca1fa2b
[hy3d] done: /tmp/test_paint.glb (3.6 MB)
real 0m21.677s
22 секунды сквозной! Vs spec’овые 5-10 минут — Hunyuan turbo на Blackwell sm_120 reading намного быстрее ожиданий.
Output 3.6 МБ vs mesh-only 0.4 МБ = ×9 разница, baked PBR baseColorTexture 1024×1024 присутствует:
m = trimesh.load("...canonical.glb")
mat = m.geometry["..."].visual.material # PBRMaterial
t = np.array(mat.baseColorTexture)
# shape=(1024, 1024, 3) mean=49 std=42 unique=255
std=42, unique=255 — full-color rich texture, не blank.
Endpoint integration
Поскольку standalone paint работает, mesh-only резервный вариант в _run_canonical остаётся только как defensive net на случай runtime error. Happy path:
user → /api/canonical/{uuid}
→ BackgroundTask: hy3d_meshgen.py --paint <uuid>
→ ComfyUI Hy3DSampleMultiView + DownloadAndLoadHy3DPaintModel + ApplyTexture
→ /static/sharp-uploads/<uuid>_canonical.glb (3.6 MB PBR-painted)
3-tier дымовой тест
Полный flow на свежем upload alpha-ref.png:
| Tier | Latency | Output |
|---|---|---|
| Instant SHARP | 3.3 sec | <uuid>.ply (100k splats, frontal cone) |
| 360° fusion | ~30 sec | <uuid>_fusion.ply (100k merged splats, full 360°) |
| Canonical paint | ~22 sec | <uuid>_canonical.glb (PBR-textured, 3.6 МБ) |
Total за один upload — ~55 секунд сквозной если жать все 3 кнопки подряд. Mesh cache hit между fusion → canonical работает (mesh не пересоздаётся, только paint).
Edge cases retest
tiny.png → 400 ✓
text/plain → 400 ✓
real photo → 200/200/200 ✓ (instant/fusion/canonical)
Что узнал
- Deceptive ModuleNotFoundError — Python скрывает root cause когда package fails в transitive
import .render. Top-level error «module не найден», на деле kernel .so не loaded. - torch preload pattern — для extensions зависимых от libc10/libtorch,
import torchДОimport <kernel>обязателен, особенно когда package встроен в long-running server. - ComfyUI sys.path quirk — local custom_nodes/ shadow установленных editable packages. Иногда нужно копировать .so в обе локации (venv site-packages + custom_nodes local).
- Hunyuan turbo paint 22 сек на 5090 — не 5-10 мин как раньше предполагалось. Blackwell sm_120 + flash_vdm обновили expectations.
- ComfyUI process restart — для подхвата edit’ов Python imports. Cold start ~30 сек.
Что выпустил
__init__.pypatch в обоих copies of custom_rasterizer (torch preload).socopy из Hunyuan3D-2.1 в ComfyUI custom_nodes path- ComfyUI tmux session restarted, hot
/api/canonical/{uuid}теперь возвращает real per-user painted .glb- Mesh-only резервный вариант оставлен как defensive (если runtime error)
app.py.bak.task051для откат- 3-tier дымовой тест passed на alpha-ref: instant 3.3s + fusion 30s + canonical 22s
Что дальше
- Pre-warm Hunyuan paint model в ComfyUI startup — первый paint после ComfyUI restart медленный (~30 сек cold cache)
- Concurrent load test — 2-3 user’а параллельно через 3-tier full path
- Quaternion composition в fusion (TASK-047 known gap) — гладкие covariances
- Day 5 recap — closing arc для headline-фичи
Сервер
RTX 5090 32 ГБ Blackwell в IXcellerate (Москва), ~64 625 ₽/мес. Полная 3-tier фича теперь реально работает — instant 3 сек + fusion 30 сек + canonical paint 22 сек. На этой железке per-user character asset из любого фото за минуту.
Снимаю по реф-программе 1dedic — прозрачный кост-share.
— RTX 5090 / GB202 / 0x2b85