From 14c8414025aeafe65d1ff579589ddf9a45b5fb52 Mon Sep 17 00:00:00 2001
From: Vladimir Mandic
Date: Mon, 25 Nov 2024 21:02:46 -0500
Subject: [PATCH] update stats and previews
Signed-off-by: Vladimir Mandic
---
CHANGELOG.md | 3 ++
extensions-builtin/sdnext-modernui | 2 +-
javascript/progressBar.js | 6 +++-
modules/control/run.py | 7 ++--
modules/ui_common.py | 1 +
modules/ui_control.py | 57 ++++++++++++++++++++++--------
6 files changed, 54 insertions(+), 22 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 170c780a5..3ff4f0944 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,9 @@
- improved live preview display and performance
- improved accordion behavior
- auto-size networks height for sidebar
+ - control: hide preview column by default
+ - control: optionn to hide input column
+ - control: add stats
- browser->server logging framework
- Fixes:
- update `diffusers`
diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui
index ea7062d27..3008cee4b 160000
--- a/extensions-builtin/sdnext-modernui
+++ b/extensions-builtin/sdnext-modernui
@@ -1 +1 @@
-Subproject commit ea7062d27d11ee50a0f22a34753f81a9c9e9d57c
+Subproject commit 3008cee4b67bb00f8f1a4fe4510ec27ba92aa418
diff --git a/javascript/progressBar.js b/javascript/progressBar.js
index ff9be4666..c385fe5db 100644
--- a/javascript/progressBar.js
+++ b/javascript/progressBar.js
@@ -71,6 +71,8 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
if (!parentGallery) return;
const footers = Array.from(gradioApp().querySelectorAll('.gallery_footer'));
for (const footer of footers) footer.style.display = 'none'; // remove all footers
+ const galleries = Array.from(gradioApp().querySelectorAll('.gallery_main'));
+ for (const gallery of galleries) gallery.style.display = 'none'; // remove all footers
livePreview = document.createElement('div');
livePreview.className = 'livePreview';
@@ -89,7 +91,9 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
localStorage.removeItem('task');
setProgress();
const footers = Array.from(gradioApp().querySelectorAll('.gallery_footer'));
- for (const footer of footers) footer.style.display = 'flex'; // remove all footers
+ for (const footer of footers) footer.style.display = 'flex'; // restore all footers
+ const galleries = Array.from(gradioApp().querySelectorAll('.gallery_main'));
+ for (const gallery of galleries) gallery.style.display = 'flex'; // remove all galleries
try {
if (parentGallery && livePreview) {
parentGallery.removeChild(livePreview);
diff --git a/modules/control/run.py b/modules/control/run.py
index 5d6343c98..88dddc213 100644
--- a/modules/control/run.py
+++ b/modules/control/run.py
@@ -717,14 +717,11 @@ def control_run(state: str = '',
shared.log.error(f'Control pipeline failed: type={unit_type} units={len(active_model)} error={e}')
errors.display(e, 'Control')
- t_end = time.time()
-
if len(output_images) == 0:
output_images = None
image_txt = '| Images None'
else:
- image_str = [f'{image.width}x{image.height}' for image in output_images]
- image_txt = f'| Time {t_end-t_start:.2f}s | Images {len(output_images)} | Size {" ".join(image_str)}'
+ image_txt = ''
p.init_images = output_images # may be used for hires
if video_type != 'None' and isinstance(output_images, list):
@@ -738,7 +735,7 @@ def control_run(state: str = '',
restore_pipeline()
debug(f'Ready: {image_txt}')
- html_txt = f'Ready {image_txt}
'
+ html_txt = f'Ready {image_txt}
' if image_txt != '' else ''
if len(info_txt) > 0:
html_txt = html_txt + infotext_to_html(info_txt[0])
if is_generator:
diff --git a/modules/ui_common.py b/modules/ui_common.py
index e21033718..3e7c68bec 100644
--- a/modules/ui_common.py
+++ b/modules/ui_common.py
@@ -255,6 +255,7 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None):
object_fit='scale-down',
height=height,
elem_id=f"{tabname}_gallery",
+ elem_classes=["gallery_main"],
)
if prompt is not None:
interrogate_clip_btn, interrogate_booru_btn = ui_sections.create_interrogate_buttons('control')
diff --git a/modules/ui_control.py b/modules/ui_control.py
index 0bf070036..f4329663a 100644
--- a/modules/ui_control.py
+++ b/modules/ui_control.py
@@ -9,7 +9,7 @@ from modules.control.units import xs # vislearn ControlNet-XS
from modules.control.units import lite # vislearn ControlNet-XS
from modules.control.units import t2iadapter # TencentARC T2I-Adapter
from modules.control.units import reference # reference pipeline
-from modules import errors, shared, progress, ui_components, ui_symbols, ui_common, ui_sections, generation_parameters_copypaste, call_queue, scripts, masking, images, processing_vae # pylint: disable=ungrouped-imports
+from modules import errors, shared, progress, ui_components, ui_symbols, ui_common, ui_sections, generation_parameters_copypaste, call_queue, scripts, masking, images, processing_vae, timer # pylint: disable=ungrouped-imports
from modules import ui_control_helpers as helpers
@@ -21,13 +21,36 @@ debug = shared.log.trace if os.environ.get('SD_CONTROL_DEBUG', None) is not None
debug('Trace: CONTROL')
-def return_controls(res):
+def return_stats(t: float = None):
+ if t is None:
+ elapsed_text = ''
+ else:
+ elapsed = time.perf_counter() - t
+ elapsed_m = int(elapsed // 60)
+ elapsed_s = elapsed % 60
+ elapsed_text = f"Time: {elapsed_m}m {elapsed_s:.2f}s |" if elapsed_m > 0 else f"Time: {elapsed_s:.2f}s |"
+ summary = timer.process.summary(min_time=0.1, total=False).replace('=', ' ')
+ vram_html = ''
+ if not shared.mem_mon.disabled:
+ vram = {k: -(v//-(1024*1024)) for k, v in shared.mem_mon.read().items()}
+ used = round(100 * vram['used'] / (vram['total'] + 0.001))
+ if vram.get('active_peak', 0) > 0:
+ vram_html += f"| GPU {max(vram['active_peak'], vram['reserved_peak'])} MB {used}%"
+ vram_html += f" | retries {vram['retries']} oom {vram['oom']}" if vram.get('retries', 0) > 0 or vram.get('oom', 0) > 0 else ''
+ return f""
+
+
+def return_controls(res, t: float = None):
# return preview, image, video, gallery, text
debug(f'Control received: type={type(res)} {res}')
+ if t is None:
+ perf = ''
+ else:
+ perf = return_stats(t)
if res is None: # no response
- return [None, None, None, None, '']
+ return [None, None, None, None, '', perf]
elif isinstance(res, str): # error response
- return [None, None, None, None, res]
+ return [None, None, None, None, res, perf]
elif isinstance(res, tuple): # standard response received as tuple via control_run->yield(output_images, process_image, result_txt)
preview_image = res[1] # may be None
output_image = res[0][0] if isinstance(res[0], list) else res[0] # may be image or list of images
@@ -37,9 +60,9 @@ def return_controls(res):
output_gallery = [res[0]] if res[0] is not None else [] # must return list, but can receive single image
result_txt = res[2] if len(res) > 2 else '' # do we have a message
output_video = res[3] if len(res) > 3 else None # do we have a video filename
- return [preview_image, output_image, output_video, output_gallery, result_txt]
+ return [preview_image, output_image, output_video, output_gallery, result_txt, perf]
else: # unexpected
- return [None, None, None, None, f'Control: Unexpected response: {type(res)}']
+ return [None, None, None, None, f'Control: Unexpected response: {type(res)}', perf]
def get_units(*values):
@@ -67,17 +90,18 @@ def generate_click(job_id: str, state: str, active_tab: str, *args):
shared.state.begin('Generate')
progress.add_task_to_queue(job_id)
with call_queue.queue_lock:
- yield [None, None, None, None, 'Control: starting']
+ yield [None, None, None, None, 'Control: starting', '']
shared.mem_mon.reset()
progress.start_task(job_id)
try:
+ t = time.perf_counter()
for results in control_run(state, units, helpers.input_source, helpers.input_init, helpers.input_mask, active_tab, True, *args):
progress.record_results(job_id, results)
- yield return_controls(results)
+ yield return_controls(results, t)
except Exception as e:
shared.log.error(f"Control exception: {e}")
errors.display(e, 'Control')
- yield [None, None, None, None, f'Control: Exception: {e}']
+ yield [None, None, None, None, f'Control: Exception: {e}', '']
progress.finish_task(job_id)
shared.state.end()
@@ -106,7 +130,8 @@ def create_ui(_blocks: gr.Blocks=None):
with gr.Accordion(open=False, label="Input", elem_id="control_input", elem_classes=["small-accordion"]):
with gr.Row():
- show_preview = gr.Checkbox(label="Show preview", value=True, elem_id="control_show_preview")
+ show_input = gr.Checkbox(label="Show input", value=True, elem_id="control_show_input")
+ show_preview = gr.Checkbox(label="Show preview", value=False, elem_id="control_show_preview")
with gr.Row():
input_type = gr.Radio(label="Input type", choices=['Control only', 'Init image same as control', 'Separate init image'], value='Control only', type='index', elem_id='control_input_type')
with gr.Row():
@@ -153,13 +178,13 @@ def create_ui(_blocks: gr.Blocks=None):
override_settings = ui_common.create_override_inputs('control')
with gr.Row(variant='compact', elem_id="control_extra_networks", elem_classes=["extra_networks_root"], visible=False) as extra_networks_ui:
- from modules import timer, ui_extra_networks
+ from modules import ui_extra_networks
extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, btn_extra, 'control', skip_indexing=shared.opts.extra_network_skip_indexing)
timer.startup.record('ui-networks')
with gr.Row(elem_id='control-inputs'):
- with gr.Column(scale=9, elem_id='control-input-column', visible=True) as _column_input:
- gr.HTML('Control input
')
+ with gr.Column(scale=9, elem_id='control-input-column', visible=True) as column_input:
+ gr.HTML('Input')
with gr.Tabs(elem_classes=['control-tabs'], elem_id='control-tab-input'):
with gr.Tab('Image', id='in-image') as tab_image:
input_mode = gr.Label(value='select', visible=False)
@@ -190,12 +215,12 @@ def create_ui(_blocks: gr.Blocks=None):
gr.HTML('Output')
with gr.Tabs(elem_classes=['control-tabs'], elem_id='control-tab-output') as output_tabs:
with gr.Tab('Gallery', id='out-gallery'):
- output_gallery, _output_gen_info, _output_html_info, _output_html_info_formatted, _output_html_log = ui_common.create_output_panel("control", preview=True, prompt=prompt, height=gr_height)
+ output_gallery, _output_gen_info, _output_html_info, _output_html_info_formatted, output_html_log = ui_common.create_output_panel("control", preview=True, prompt=prompt, height=gr_height)
with gr.Tab('Image', id='out-image'):
output_image = gr.Image(label="Output", show_label=False, type="pil", interactive=False, tool="editor", height=gr_height, elem_id='control_output_image', elem_classes=['control-image'])
with gr.Tab('Video', id='out-video'):
output_video = gr.Video(label="Output", show_label=False, height=gr_height, elem_id='control_output_video', elem_classes=['control-image'])
- with gr.Column(scale=9, elem_id='control-preview-column', visible=True) as column_preview:
+ with gr.Column(scale=9, elem_id='control-preview-column', visible=False) as column_preview:
gr.HTML('Preview')
with gr.Tabs(elem_classes=['control-tabs'], elem_id='control-tab-preview'):
with gr.Tab('Preview', id='preview-image') as _tab_preview:
@@ -498,6 +523,7 @@ def create_ui(_blocks: gr.Blocks=None):
btn_update = gr.Button('Update', interactive=True, visible=False, elem_id='control_update')
btn_update.click(fn=get_units, inputs=controls, outputs=[], show_progress=True, queue=False)
+ show_input.change(fn=lambda x: gr.update(visible=x), inputs=[show_input], outputs=[column_input])
show_preview.change(fn=lambda x: gr.update(visible=x), inputs=[show_preview], outputs=[column_preview])
input_type.change(fn=lambda x: gr.update(visible=x == 2), inputs=[input_type], outputs=[column_init])
btn_prompt_counter.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[prompt, steps], outputs=[prompt_counter])
@@ -550,6 +576,7 @@ def create_ui(_blocks: gr.Blocks=None):
output_video,
output_gallery,
result_txt,
+ output_html_log,
]
control_dict = dict(
fn=generate_click,