hvoss-techfak commited on
Commit
2c24bbf
·
1 Parent(s): 643e04c

limiting gpu usage by moving autoforge into its own function

Browse files
Files changed (1) hide show
  1. app.py +67 -65
app.py CHANGED
@@ -310,6 +310,28 @@ if os.path.exists(DEFAULT_MATERIALS_CSV):
310
  else:
311
  initial_df.to_csv(DEFAULT_MATERIALS_CSV, index=False)
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
  # Helper for creating an empty 10-tuple for error returns
315
  def create_empty_error_outputs(log_message=""):
@@ -625,14 +647,14 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
625
  )
626
 
627
  with gr.Row():
628
- download_zip = gr.File( # was visible=True
629
- label="Download all results (.zip)",
 
630
  interactive=True,
631
  visible=False,
632
  )
633
 
634
  # --- Backend Function for Running the Script ---
635
- @spaces.GPU(duration=120)
636
  def execute_autoforge_script(
637
  current_filaments_df_state_val, input_image, *accordion_param_values
638
  ):
@@ -737,40 +759,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
737
  "extra": {"command": cmd_str}, # still searchable
738
  }
739
  )
740
- process = subprocess.Popen(
741
- command,
742
- stdout=subprocess.PIPE,
743
- stderr=subprocess.PIPE,
744
- text=True,
745
- bufsize=1,
746
- universal_newlines=True,
747
- )
748
-
749
- # ---- helper: read stdout in a background thread -------------------
750
- from threading import Thread
751
- from queue import Queue, Empty
752
-
753
- def _enqueue(pipe, q):
754
- """Forward stdout/stderr to a queue, emitting on both '\n' and '\r'."""
755
- buf = ""
756
- while True:
757
- ch = pipe.read(1) # read a single character
758
- if ch == "": # EOF
759
- if buf:
760
- q.put(buf) # flush whatever is left
761
- break
762
- buf += ch
763
- if ch in ("\n", "\r"): # tqdm uses '\r'
764
- q.put(buf)
765
- buf = ""
766
- pipe.close()
767
-
768
- q_out = Queue()
769
- Thread(target=_enqueue, args=(process.stdout, q_out), daemon=True).start()
770
- Thread(target=_enqueue, args=(process.stderr, q_out), daemon=True).start()
771
-
772
- preview_mtime = 0
773
- last_push = 0
774
 
775
  def _maybe_new_preview():
776
  """
@@ -792,28 +780,45 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
792
 
793
  return src # → refresh image
794
 
795
- # ---- main loop: poll every 0.5 s ----------------------------------
796
- while process.poll() is None or not q_out.empty():
797
- # drain whatever is waiting in stdout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
798
  try:
799
- while True:
800
- log_output += q_out.get_nowait()
 
 
 
 
801
  except Empty:
802
  pass
803
 
804
  now = time.time()
805
- if now - last_push >= 1.0: # 500 ms tick
806
  current_preview = _maybe_new_preview()
807
  yield (
808
  log_output,
809
  current_preview,
810
- gr.update(), # ### ZIP PATCH: placeholder for zip widget
811
  )
812
  last_push = now
813
 
814
  time.sleep(0.05) # keep CPU load low
815
 
816
- return_code = process.wait()
817
  if return_code != 0:
818
  err = RuntimeError(f"Autoforge exited with code {return_code} \n {log_output}")
819
  capture_exception(err) # send to Sentry
@@ -833,22 +838,17 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
833
  }
834
  )
835
 
836
- # make sure we show the final preview (if any)
837
- final_preview = _maybe_new_preview() or os.path.join(
838
- run_output_dir_val, "final_model.png"
839
- )
840
-
841
- zip_path = zip_dir_no_compress(
842
- run_output_dir_val,
843
- os.path.join(run_output_dir_val, "autoforge_results.zip"),
844
- )
845
-
846
- # 4. Prepare output file paths
847
  png_path = os.path.join(run_output_dir_val, "final_model.png")
848
- stl_path = os.path.join(run_output_dir_val, "final_model.stl")
849
- txt_path = os.path.join(run_output_dir_val, "swap_instructions.txt")
850
- hfp_path = os.path.join(run_output_dir_val, "project_file.hfp")
851
-
852
  out_png = png_path if os.path.exists(png_path) else None
853
 
854
  if out_png is None:
@@ -856,10 +856,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
856
 
857
  yield (
858
  log_output, # progress_output
859
- out_png, # final_image_preview
860
- gr.update(
861
- value=zip_path, visible=True, interactive=True
862
- ), # ### ZIP PATCH: download_zip
 
 
863
  )
864
 
865
  run_inputs = [filament_df_state, input_image_component] + [
@@ -868,7 +870,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
868
  run_outputs = [
869
  progress_output,
870
  final_image_preview,
871
- download_zip, # ### ZIP PATCH: only three outputs now
872
  ]
873
 
874
  run_button.click(execute_autoforge_script, inputs=run_inputs, outputs=run_outputs)
 
310
  else:
311
  initial_df.to_csv(DEFAULT_MATERIALS_CSV, index=False)
312
 
313
+ @spaces.GPU()
314
+ def run_autoforge_process(cmd, q):
315
+ """
316
+ Start the Autoforge CLI, stream its stdout to Queue *q*,
317
+ then drop a sentinel ('__RC__', return_code) at the end.
318
+ """
319
+ import subprocess, os, sys
320
+
321
+ proc = subprocess.Popen(
322
+ cmd,
323
+ stdout=subprocess.PIPE,
324
+ stderr=subprocess.STDOUT,
325
+ text=True,
326
+ bufsize=1,
327
+ universal_newlines=True,
328
+ )
329
+
330
+ for line in proc.stdout: # line-by-line streaming
331
+ q.put(line)
332
+ proc.wait()
333
+ q.put(("__RC__", proc.returncode))
334
+
335
 
336
  # Helper for creating an empty 10-tuple for error returns
337
  def create_empty_error_outputs(log_message=""):
 
647
  )
648
 
649
  with gr.Row():
650
+ download_results = gr.File(
651
+ label="Download results",
652
+ file_count="multiple",
653
  interactive=True,
654
  visible=False,
655
  )
656
 
657
  # --- Backend Function for Running the Script ---
 
658
  def execute_autoforge_script(
659
  current_filaments_df_state_val, input_image, *accordion_param_values
660
  ):
 
759
  "extra": {"command": cmd_str}, # still searchable
760
  }
761
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
762
 
763
  def _maybe_new_preview():
764
  """
 
780
 
781
  return src # → refresh image
782
 
783
+ # ---- run Autoforge on the GPU in a helper thread ------------------
784
+ from threading import Thread
785
+ from queue import Queue, Empty
786
+
787
+ q_out = Queue()
788
+ worker = Thread(
789
+ target=run_autoforge_process,
790
+ args=(command, q_out),
791
+ daemon=True,
792
+ )
793
+ worker.start()
794
+
795
+ preview_mtime = 0
796
+ last_push = 0
797
+ return_code = None
798
+
799
+ while worker.is_alive() or not q_out.empty():
800
  try:
801
+ while True: # drain whatever is ready
802
+ msg = q_out.get_nowait()
803
+ if isinstance(msg, tuple): # ('__RC__', code) sentinel
804
+ return_code = msg[1]
805
+ else:
806
+ log_output += msg
807
  except Empty:
808
  pass
809
 
810
  now = time.time()
811
+ if now - last_push >= 1.0: # 1-second UI tick
812
  current_preview = _maybe_new_preview()
813
  yield (
814
  log_output,
815
  current_preview,
816
+ gr.update(), # placeholder for download widget
817
  )
818
  last_push = now
819
 
820
  time.sleep(0.05) # keep CPU load low
821
 
 
822
  if return_code != 0:
823
  err = RuntimeError(f"Autoforge exited with code {return_code} \n {log_output}")
824
  capture_exception(err) # send to Sentry
 
838
  }
839
  )
840
 
841
+ files_to_offer = [
842
+ p
843
+ for p in [
844
+ os.path.join(run_output_dir_val, "final_model.png"),
845
+ os.path.join(run_output_dir_val, "final_model.stl"),
846
+ os.path.join(run_output_dir_val, "swap_instructions.txt"),
847
+ os.path.join(run_output_dir_val, "project_file.hfp"),
848
+ ]
849
+ if os.path.exists(p)
850
+ ]
 
851
  png_path = os.path.join(run_output_dir_val, "final_model.png")
 
 
 
 
852
  out_png = png_path if os.path.exists(png_path) else None
853
 
854
  if out_png is None:
 
856
 
857
  yield (
858
  log_output, # progress_output
859
+ out_png, # final_image_preview (same as before)
860
+ gr.update( # download_results
861
+ value=files_to_offer,
862
+ visible=True,
863
+ interactive=True,
864
+ ),
865
  )
866
 
867
  run_inputs = [filament_df_state, input_image_component] + [
 
870
  run_outputs = [
871
  progress_output,
872
  final_image_preview,
873
+ download_results, # ### ZIP PATCH: only three outputs now
874
  ]
875
 
876
  run_button.click(execute_autoforge_script, inputs=run_inputs, outputs=run_outputs)