diff --git a/javascript/progressBar.js b/javascript/progressBar.js index afb9259dc..3f5c9fcef 100644 --- a/javascript/progressBar.js +++ b/javascript/progressBar.js @@ -61,8 +61,8 @@ function randomId() { function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgress = null, once = false) { localStorage.setItem('task', id_task); let hasStarted = false; - const dateStart = new Date(); - const prevProgress = null; + let dateStart = new Date(); + let prevProgress = null; const parentGallery = galleryEl ? galleryEl.parentNode : null; let livePreview; let img; @@ -113,30 +113,36 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres if (!opts.live_previews_enable || opts.live_preview_refresh_period === 0 || opts.show_progress_every_n_steps === 0) return; const onProgressHandler = (res) => { - // debug('onProgress', res); + if (res?.debug) debug('livePreview:', dateStart, res); lastState = res; const elapsedFromStart = (new Date() - dateStart) / 1000; hasStarted |= res.active; if (res.completed || (!res.active && (hasStarted || once)) || (elapsedFromStart > 30 && !res.queued && res.progress === prevProgress)) { - // debug('onProgressEnd', res); + if (res?.debug) debug('livePreview end:', res); done(); return; } + if (res.progress !== prevProgress) { + dateStart = new Date(); + prevProgress = res.progress; + } setProgress(res); if (res.live_preview && !livePreview) initLivePreview(); if (res.live_preview && galleryEl) { if (img.src !== res.live_preview) img.src = res.live_preview; + id_live_preview = res.id_live_preview; } if (onProgress) onProgress(res); setTimeout(() => start(id_task, id_live_preview), opts.live_preview_refresh_period || 500); }; const onProgressErrorHandler = (err) => { - error(`onProgressError: ${err}`); + error(`livePreview: ${err}`); done(); }; xhrPost('./internal/progress', { id_task, id_live_preview }, onProgressHandler, onProgressErrorHandler, false, 5000); }; + debug('livePreview start:', dateStart); start(id_task, 0); } diff --git a/modules/progress.py b/modules/progress.py index d18d1ee9f..68102b915 100644 --- a/modules/progress.py +++ b/modules/progress.py @@ -1,4 +1,5 @@ import base64 +import os import io import time from pydantic import BaseModel, Field # pylint: disable=no-name-in-module @@ -10,6 +11,8 @@ pending_tasks = {} finished_tasks = [] recorded_results = [] recorded_results_limit = 2 +debug = os.environ.get('SD_PREVIEW_DEBUG', None) is not None +debug_log = shared.log.trace if debug else lambda *args, **kwargs: None def start_task(id_task): @@ -48,6 +51,7 @@ class InternalProgressResponse(BaseModel): queued: bool = Field(title="Whether the task is in queue") paused: bool = Field(title="Whether the task is paused") completed: bool = Field(title="Whether the task has already finished") + debug: bool = Field(title="Debug logging level") progress: float = Field(default=None, title="Progress", description="The progress with a range of 0 to 1") eta: float = Field(default=None, title="ETA in secs") live_preview: str = Field(default=None, title="Live preview image", description="Current live preview; a data: uri") @@ -60,8 +64,6 @@ def progressapi(req: ProgressRequest): queued = req.id_task in pending_tasks completed = req.id_task in finished_tasks paused = shared.state.paused - if not active: - return InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, id_live_preview=-1, textinfo="Queued..." if queued else "Waiting...") shared.state.job_count = max(shared.state.frame_count, shared.state.job_count, shared.state.job_no) batch_x = max(shared.state.job_no, 0) batch_y = max(shared.state.job_count, 1) @@ -75,14 +77,17 @@ def progressapi(req: ProgressRequest): eta = predicted - elapsed if predicted is not None else None id_live_preview = req.id_live_preview live_preview = None - shared.state.set_current_image() - if shared.opts.live_previews_enable and (shared.state.id_live_preview != req.id_live_preview) and (shared.state.current_image is not None): + updated = shared.state.set_current_image() + debug_log(f'Preview: job={shared.state.job} active={active} progress={current}/{total} request={id_live_preview} last={shared.state.id_live_preview} enabled={shared.opts.live_previews_enable} updated={updated} image={shared.state.current_image} elapsed={elapsed:.3f}') + if not active: + return InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, id_live_preview=-1, debug=debug, textinfo="Queued..." if queued else "Waiting...") + if shared.opts.live_previews_enable and (shared.state.id_live_preview != id_live_preview) and (shared.state.current_image is not None): buffered = io.BytesIO() shared.state.current_image.save(buffered, format='jpeg') live_preview = f'data:image/jpeg;base64,{base64.b64encode(buffered.getvalue()).decode("ascii")}' - id_live_preview = shared.state.id_live_preview + id_live_preview = shared.state.id_live_preview - res = InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo) + res = InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, debug=debug, textinfo=shared.state.textinfo) return res diff --git a/modules/shared_state.py b/modules/shared_state.py index 024427f09..3fe0279f1 100644 --- a/modules/shared_state.py +++ b/modules/shared_state.py @@ -149,16 +149,17 @@ class State: def set_current_image(self): if self.job == 'VAE': # avoid generating preview while vae is running - return + return False from modules.shared import opts, cmd_opts - if cmd_opts.lowvram or self.api or not opts.live_previews_enable or opts.show_progress_every_n_steps <= 0: - return - if not self.disable_preview and (abs(self.sampling_step - self.current_image_sampling_step) >= opts.show_progress_every_n_steps): - self.do_set_current_image() + if cmd_opts.lowvram or self.api or (not opts.live_previews_enable) or (opts.show_progress_every_n_steps <= 0): + return False + if (not self.disable_preview) and (abs(self.sampling_step - self.current_image_sampling_step) >= opts.show_progress_every_n_steps): + return self.do_set_current_image() + return False def do_set_current_image(self): - if self.current_latent is None or self.disable_preview or self.preview_busy: - return + if (self.current_latent is None) or self.disable_preview or self.preview_busy: + return False from modules import shared, sd_samplers self.preview_busy = True try: @@ -175,10 +176,13 @@ class State: """ image = sd_samplers.samples_to_image_grid(sample) if shared.opts.show_progress_grid else sd_samplers.sample_to_image(sample) self.assign_current_image(image) + self.preview_busy = False + return True except Exception as e: + self.preview_busy = False log.error(f'State image: last={self.id_live_preview} step={self.sampling_step} {e}') display(e, 'State image') - self.preview_busy = False + return False def assign_current_image(self, image): self.current_image = image