luh1124 commited on
Commit
66f3e6b
Β·
1 Parent(s): 19a8e83

fix(app): split renderings into two @GPU callbacks; lower defaults

Browse files

- Split generate_renderings into generate_videos + generate_glb.
Each gets its own @GPU callback so the heavy mesh simplification
in GLB export does not blow the 120 s ZeroGPU lease.
- Lower default num_cam / num_hdri from 36 β†’ 24.
- Lower default texture_size from 2048 β†’ 1024.
- UI: make both buttons visible and wire them independently.

Files changed (1) hide show
  1. app.py +92 -65
app.py CHANGED
@@ -534,7 +534,7 @@ def load_slat_file(
534
 
535
  @GPU
536
  @torch.no_grad()
537
- def generate_renderings(
538
  asset_state: Dict[str, Any],
539
  hdri_file_obj: Any,
540
  hdri_rot: float,
@@ -545,29 +545,98 @@ def generate_renderings(
545
  pitch: float,
546
  fov: float,
547
  radius: float,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  simplify: float,
549
  texture_size: int,
550
  req: gr.Request,
551
  progress: gr.Progress = gr.Progress(track_tqdm=True),
552
- ) -> tuple[str, str, str, str, str]:
553
- """β‘’β‘£ Unified rendering pipeline: videos + PBR GLB in ONE CUDA context.
554
-
555
- This merged callback avoids ZeroGPU CUDA context resets between steps.
556
- Returns: (cam_video, hdri_video, roll_video, pbr_glb, status_msg)
 
557
  """
558
  slat_path = _require_slat(asset_state)
559
  hdri_path = _require_hdri(hdri_file_obj)
560
  session_dir = CACHE_DIR / str(req.session_hash)
561
 
562
- # ── Load NeAR once (fresh CUDA context) ───────────────────────────
563
  progress(0.05, desc="Loading NeAR on GPU")
564
  pipe = _ensure_near_on_cuda()
565
 
566
- progress(0.08, desc="Loading SLaT / HDRI")
567
  slat = pipe.load_slat(slat_path)
568
  hdri_np = _load_hdri_resized(pipe, hdri_path)
569
 
570
- # ── Prepare base mesh (optional, for GLB export) ────────────────────
571
  mesh_path = asset_state.get("mesh_path")
572
  base_mesh: Optional[trimesh.Trimesh] = None
573
  if mesh_path and os.path.isfile(mesh_path):
@@ -579,46 +648,7 @@ def generate_renderings(
579
  else:
580
  print("[NeAR] no mesh_path β€” will use SLaT decoder mesh for GLB", flush=True)
581
 
582
- # ── Render videos ──────────────────────────────────────────────────
583
- progress(0.12, desc="Rendering camera-orbit video…")
584
- with torch.no_grad():
585
- cam_frames = pipe.render_camera_path_video(
586
- slat, hdri_np,
587
- num_views=int(num_cam),
588
- fov=float(fov), radius=float(radius),
589
- hdri_rot_deg=float(hdri_rot),
590
- full_video=True,
591
- shadow_video=True,
592
- bg_color=(1, 1, 1),
593
- verbose=True,
594
- )
595
- p_cam = session_dir / "video_camera_orbit.mp4"
596
- imageio.mimsave(p_cam, cam_frames, fps=int(fps))
597
- del cam_frames
598
- _free_cuda() # Free GPU mem temporarily while still in same CUDA context
599
-
600
- progress(0.55, desc="Rendering HDRI-rotation video…")
601
- with torch.no_grad():
602
- roll_frames, hdri_frames = pipe.render_hdri_rotation_video(
603
- slat, hdri_np,
604
- num_frames=int(num_hdri),
605
- yaw_deg=float(yaw), pitch_deg=float(pitch),
606
- fov=float(fov), radius=float(radius),
607
- full_video=True,
608
- shadow_video=True,
609
- bg_color=(1, 1, 1),
610
- verbose=True,
611
- )
612
- p_hdri = session_dir / "video_hdri_rotation.mp4"
613
- p_roll = session_dir / "video_env_roll.mp4"
614
- imageio.mimsave(p_hdri, hdri_frames, fps=int(fps))
615
- imageio.mimsave(p_roll, roll_frames, fps=int(fps))
616
- del hdri_frames, roll_frames
617
- _free_cuda()
618
-
619
- # ── Export PBR GLB ────────────────────────────────────────────────
620
- # (renderer/tone_mapper still valid in same CUDA context)
621
- progress(0.85, desc="Baking PBR GLB…")
622
  glb = pipe.export_glb_from_slat(
623
  slat, hdri_np,
624
  hdri_rot_deg=float(hdri_rot),
@@ -634,15 +664,10 @@ def generate_renderings(
634
  glb.export(out_path)
635
  del glb
636
 
637
- # ── Cleanup: tear down NeAR before exiting CUDA callback ────────────
638
  _teardown_near()
639
 
640
- msg = (
641
- f"**β‘’ Videos ready** β†’ `{Path(p_cam).name}`, "
642
- f"`{Path(p_hdri).name}`, `{Path(p_roll).name}` \n\n"
643
- f"**β‘£ PBR GLB ready** β†’ `{Path(out_path).name}`"
644
- )
645
- return str(p_cam), str(p_hdri), str(p_roll), str(out_path), msg
646
 
647
 
648
  def _hunyuan_mesh_to_renderer_space(mesh: trimesh.Trimesh) -> trimesh.Trimesh:
@@ -821,12 +846,12 @@ def build_app() -> gr.Blocks:
821
  hdri_rot = gr.Slider(0, 360, value=0, step=1, label="HDRI rotation Β°")
822
 
823
  gr.HTML('<p class="section-kicker" style="margin:2px 0 2px;padding:0">Actions</p>')
824
- btn_videos = gr.Button("β‘’β‘£ Generate Videos & GLB", variant="primary")
825
- btn_glb = gr.Button("β‘£ Export PBR GLB", variant="primary", visible=False)
826
  with gr.Accordion("Video / export settings", open=False):
827
  fps = gr.Slider(8, 48, value=24, step=1, label="FPS")
828
- num_cam = gr.Slider(8, 96, value=36, step=4, label="Camera-orbit frames")
829
- num_hdri = gr.Slider(8, 96, value=36, step=4, label="HDRI-rotation frames")
830
  with gr.Row():
831
  yaw = gr.Slider(0, 360, value=0, step=1, label="Yaw Β°")
832
  pitch = gr.Slider(-90, 90, value=0, step=1, label="Pitch Β°")
@@ -834,7 +859,7 @@ def build_app() -> gr.Blocks:
834
  fov = gr.Slider(10, 70, value=40, step=1, label="FoV")
835
  radius = gr.Slider(1.0, 4.0, value=2.0, step=0.05, label="Radius")
836
  simplify = gr.Slider(0.80, 0.99, value=0.95, step=0.01, label="GLB simplify ratio")
837
- tex_size = gr.Slider(512, 4096, value=2048, step=512, label="Texture resolution")
838
  btn_clear = gr.Button("Clear session cache", variant="secondary")
839
 
840
  # ── CENTER: linear output area ────────────────────────────────────
@@ -888,10 +913,12 @@ def build_app() -> gr.Blocks:
888
  [asset_state, status])
889
  btn_load_slat.click(load_slat_file, [slat_upload, slat_path_txt],
890
  [asset_state, status])
891
- # β‘’β‘£ Unified render callback: videos + GLB in ONE CUDA context
892
- btn_videos.click(generate_renderings,
893
- [asset_state, hdri_file, hdri_rot, fps, num_cam, num_hdri, yaw, pitch, fov, radius, simplify, tex_size],
894
- [vid_cam, vid_hdri, vid_roll, glb_view, status])
 
 
895
  btn_clear.click(clear_cache, [], [status])
896
 
897
  return demo
 
534
 
535
  @GPU
536
  @torch.no_grad()
537
+ def generate_videos(
538
  asset_state: Dict[str, Any],
539
  hdri_file_obj: Any,
540
  hdri_rot: float,
 
545
  pitch: float,
546
  fov: float,
547
  radius: float,
548
+ req: gr.Request,
549
+ progress: gr.Progress = gr.Progress(track_tqdm=True),
550
+ ) -> tuple[str, str, str, str]:
551
+ """β‘’ Render camera-orbit + HDRI-rotation videos.
552
+
553
+ Kept as a separate @GPU callback so it fits inside the 120 s ZeroGPU lease.
554
+ Returns: (cam_video, hdri_video, roll_video, status_msg)
555
+ """
556
+ slat_path = _require_slat(asset_state)
557
+ hdri_path = _require_hdri(hdri_file_obj)
558
+ session_dir = CACHE_DIR / str(req.session_hash)
559
+
560
+ progress(0.05, desc="Loading NeAR on GPU")
561
+ pipe = _ensure_near_on_cuda()
562
+
563
+ progress(0.20, desc="Loading SLaT / HDRI")
564
+ slat = pipe.load_slat(slat_path)
565
+ hdri_np = _load_hdri_resized(pipe, hdri_path)
566
+
567
+ # ── Camera-orbit video ────────────────────────────────────────────
568
+ progress(0.30, desc="Rendering camera-orbit video…")
569
+ cam_frames = pipe.render_camera_path_video(
570
+ slat, hdri_np,
571
+ num_views=int(num_cam),
572
+ fov=float(fov), radius=float(radius),
573
+ hdri_rot_deg=float(hdri_rot),
574
+ full_video=True,
575
+ shadow_video=True,
576
+ bg_color=(1, 1, 1),
577
+ verbose=True,
578
+ )
579
+ p_cam = session_dir / "video_camera_orbit.mp4"
580
+ imageio.mimsave(p_cam, cam_frames, fps=int(fps))
581
+ del cam_frames
582
+ _free_cuda()
583
+
584
+ # ── HDRI-rotation videos ──────────────────────────────────────────
585
+ progress(0.65, desc="Rendering HDRI-rotation video…")
586
+ roll_frames, hdri_frames = pipe.render_hdri_rotation_video(
587
+ slat, hdri_np,
588
+ num_frames=int(num_hdri),
589
+ yaw_deg=float(yaw), pitch_deg=float(pitch),
590
+ fov=float(fov), radius=float(radius),
591
+ full_video=True,
592
+ shadow_video=True,
593
+ bg_color=(1, 1, 1),
594
+ verbose=True,
595
+ )
596
+ p_hdri = session_dir / "video_hdri_rotation.mp4"
597
+ p_roll = session_dir / "video_env_roll.mp4"
598
+ imageio.mimsave(p_hdri, hdri_frames, fps=int(fps))
599
+ imageio.mimsave(p_roll, roll_frames, fps=int(fps))
600
+ del hdri_frames, roll_frames, slat, hdri_np
601
+ _free_cuda()
602
+
603
+ _teardown_near()
604
+
605
+ msg = (
606
+ f"**β‘’ Videos ready** β†’ `{Path(p_cam).name}`, "
607
+ f"`{Path(p_hdri).name}`, `{Path(p_roll).name}`"
608
+ )
609
+ return str(p_cam), str(p_hdri), str(p_roll), msg
610
+
611
+
612
+ @GPU
613
+ @torch.no_grad()
614
+ def generate_glb(
615
+ asset_state: Dict[str, Any],
616
+ hdri_file_obj: Any,
617
+ hdri_rot: float,
618
  simplify: float,
619
  texture_size: int,
620
  req: gr.Request,
621
  progress: gr.Progress = gr.Progress(track_tqdm=True),
622
+ ) -> tuple[str, str]:
623
+ """β‘£ Export PBR GLB (baked texture + simplified mesh).
624
+
625
+ Separate @GPU callback so the heavy mesh simplification does not
626
+ blow the 120 s budget shared with video rendering.
627
+ Returns: (pbr_glb, status_msg)
628
  """
629
  slat_path = _require_slat(asset_state)
630
  hdri_path = _require_hdri(hdri_file_obj)
631
  session_dir = CACHE_DIR / str(req.session_hash)
632
 
 
633
  progress(0.05, desc="Loading NeAR on GPU")
634
  pipe = _ensure_near_on_cuda()
635
 
636
+ progress(0.15, desc="Loading SLaT / HDRI / mesh")
637
  slat = pipe.load_slat(slat_path)
638
  hdri_np = _load_hdri_resized(pipe, hdri_path)
639
 
 
640
  mesh_path = asset_state.get("mesh_path")
641
  base_mesh: Optional[trimesh.Trimesh] = None
642
  if mesh_path and os.path.isfile(mesh_path):
 
648
  else:
649
  print("[NeAR] no mesh_path β€” will use SLaT decoder mesh for GLB", flush=True)
650
 
651
+ progress(0.30, desc="Baking PBR GLB…")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
  glb = pipe.export_glb_from_slat(
653
  slat, hdri_np,
654
  hdri_rot_deg=float(hdri_rot),
 
664
  glb.export(out_path)
665
  del glb
666
 
 
667
  _teardown_near()
668
 
669
+ msg = f"**β‘£ PBR GLB ready** β†’ `{Path(out_path).name}`"
670
+ return str(out_path), msg
 
 
 
 
671
 
672
 
673
  def _hunyuan_mesh_to_renderer_space(mesh: trimesh.Trimesh) -> trimesh.Trimesh:
 
846
  hdri_rot = gr.Slider(0, 360, value=0, step=1, label="HDRI rotation Β°")
847
 
848
  gr.HTML('<p class="section-kicker" style="margin:2px 0 2px;padding:0">Actions</p>')
849
+ btn_videos = gr.Button("β‘’ Generate Videos", variant="primary")
850
+ btn_glb = gr.Button("β‘£ Export PBR GLB", variant="primary")
851
  with gr.Accordion("Video / export settings", open=False):
852
  fps = gr.Slider(8, 48, value=24, step=1, label="FPS")
853
+ num_cam = gr.Slider(8, 96, value=24, step=4, label="Camera-orbit frames")
854
+ num_hdri = gr.Slider(8, 96, value=24, step=4, label="HDRI-rotation frames")
855
  with gr.Row():
856
  yaw = gr.Slider(0, 360, value=0, step=1, label="Yaw Β°")
857
  pitch = gr.Slider(-90, 90, value=0, step=1, label="Pitch Β°")
 
859
  fov = gr.Slider(10, 70, value=40, step=1, label="FoV")
860
  radius = gr.Slider(1.0, 4.0, value=2.0, step=0.05, label="Radius")
861
  simplify = gr.Slider(0.80, 0.99, value=0.95, step=0.01, label="GLB simplify ratio")
862
+ tex_size = gr.Slider(512, 2048, value=1024, step=512, label="Texture resolution")
863
  btn_clear = gr.Button("Clear session cache", variant="secondary")
864
 
865
  # ── CENTER: linear output area ────────────────────────────────────
 
913
  [asset_state, status])
914
  btn_load_slat.click(load_slat_file, [slat_upload, slat_path_txt],
915
  [asset_state, status])
916
+ btn_videos.click(generate_videos,
917
+ [asset_state, hdri_file, hdri_rot, fps, num_cam, num_hdri, yaw, pitch, fov, radius],
918
+ [vid_cam, vid_hdri, vid_roll, status])
919
+ btn_glb.click(generate_glb,
920
+ [asset_state, hdri_file, hdri_rot, simplify, tex_size],
921
+ [glb_view, status])
922
  btn_clear.click(clear_cache, [], [status])
923
 
924
  return demo