From 12bf006b00a1281214de76cc01d69584facd082c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 15 Aug 2026 19:21:51 +0200 Subject: [PATCH] improve state management Signed-off-by: Vladimir Mandic --- installer.py | 2 +- modules/devices.py | 2 +- modules/loader.py | 1 - modules/ltx/ltx_process.py | 4 +- modules/minimax/minimax_video.py | 178 ++++++++++++++++--------------- modules/progress.py | 7 +- 6 files changed, 102 insertions(+), 92 deletions(-) diff --git a/installer.py b/installer.py index eda04a677..9e7e7e4b2 100644 --- a/installer.py +++ b/installer.py @@ -584,7 +584,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all: return - target_commit = "7564fb016dabda0c943416190fc92398c50b1b20" # diffusers commit hash == 0.40.0.dev0 == 08-11-2026 + target_commit = "d5baa4fb548294f47dbca49890abd4b291204c60" # diffusers commit hash == 0.40.0.dev0 == 08-15-2026 # if args.use_rocm or args.use_zluda: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = package_spec('diffusers') diff --git a/modules/devices.py b/modules/devices.py index 114b1623a..ff7096530 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -548,7 +548,7 @@ def set_sdpa_params(): log.debug(f'Torch attention installed: flashattn={flash} sageattention={sage}') from diffusers.models import attention_dispatch as a - log.debug(f'Torch attention available: flash={a._CAN_USE_FLASH_ATTN} flash3={a._CAN_USE_FLASH_ATTN_3} aiter={a._CAN_USE_AITER_ATTN} sage={a._CAN_USE_SAGE_ATTN} flex={a._CAN_USE_FLEX_ATTN} npu={a._CAN_USE_NPU_ATTN} xla={a._CAN_USE_XLA_ATTN} xformers={a._CAN_USE_XFORMERS_ATTN} kernels={a.is_kernels_available()} sdnq=True') # pylint: disable=protected-access + log.debug(f'Torch attention available: flash={a._CAN_USE_FLASH_ATTN} flash3={a._CAN_USE_FLASH_ATTN_3} sage={a._CAN_USE_SAGE_ATTN} flex={a._CAN_USE_FLEX_ATTN} npu={a._CAN_USE_NPU_ATTN} xla={a._CAN_USE_XLA_ATTN} xformers={a._CAN_USE_XFORMERS_ATTN} kernels={a.is_kernels_available()} sdnq=True') # pylint: disable=protected-access except Exception as e: log.warning(f'Torch SDPA: {e}') diff --git a/modules/loader.py b/modules/loader.py index 14aea1c11..275afadae 100644 --- a/modules/loader.py +++ b/modules/loader.py @@ -11,7 +11,6 @@ from modules import timer, errors from modules.logger import log -log.info('Initializing: packages') initialized = False errors.install() logging.getLogger("DeepSpeed").disabled = True diff --git a/modules/ltx/ltx_process.py b/modules/ltx/ltx_process.py index 3f4ba7257..5d8836f6e 100644 --- a/modules/ltx/ltx_process.py +++ b/modules/ltx/ltx_process.py @@ -698,5 +698,5 @@ def run_ltx(task_id, progress.finish_task(task_id) p.close() - log.info(f'Processed: fn="{video_file}" frames={num_frames} fps={fps} its={its} resolution={resolution} time={t_end-t0:.2f} timers={timer.process.dct()} memory={memstats.memory_stats()}') - yield video_file, f'LTX: Generation completed | File {video_file} | Frames {num_frames} | Resolution {resolution} | f/s {fps} | it/s {its} ' + f"

{summary} {memory}

" + log.info(f'Processed: fn="{video_file}" frames={num_frames} fps={fps} its={its} resolution={resolution} time={t_end-t0:.2f} timers={timer.process.dct(no_total=True)} memory={memstats.memory_stats()}') + yield video_file, f'Video | File {video_file} | Frames {num_frames} | Resolution {resolution} | f/s {fps} | it/s {its} ' + f"

{summary} {memory}

" diff --git a/modules/minimax/minimax_video.py b/modules/minimax/minimax_video.py index 4c6482fbb..60e6ffe46 100644 --- a/modules/minimax/minimax_video.py +++ b/modules/minimax/minimax_video.py @@ -3,7 +3,7 @@ import time from PIL import Image import numpy as np from modules.logger import log -from modules import shared, devices, processing, timer, progress, paths, sd_models, scripts_manager, call_queue, memstats, processing_video +from modules import shared, devices, errors, processing, timer, progress, paths, sd_models, scripts_manager, call_queue, memstats, processing_video from modules.video_models import models_def, video_save, video_utils @@ -98,42 +98,6 @@ def generate(task_id, _ui_state, progress.start_task(task_id) memstats.reset_stats() timer.process.reset() - workflow = load_model(model) # override workflow based on loaded model - if not workflow: - progress.finish_task(task_id) - log.error('Video: model not loaded') - return None, 'Model not loaded' - p = processing.StableDiffusionProcessingVideo( - sd_model=shared.sd_model, - video_engine=engine, - video_model=model, - prompt=prompt, - styles=styles, - seed=int(seed) if seed is not None else -1, - steps=int(steps), - width=width, - height=height, - frames=frames, - do_not_save_grid=True, - do_not_save_samples=not mp4_frames, - outpath_samples=paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_video), - ops=['video'], - ) - video_minimax.apply_overrides(p, shared.sd_model, still=False, audio=audio_enable) - video_minimax.set_sampler_shift(shared.sd_model, video_shift=video_shift, audio_shift=audio_shift) - log.debug(f'Video: engine="{engine}" model="{model}" workflow={workflow} cls={shared.sd_model.__class__.__name__} shift={video_shift}:{audio_shift} kwargs={p.task_args}') - processing.fix_seed(p) - p.ops.append('video') - p.scripts = scripts_manager.scripts_video - p.script_args = args - - prepare_inputs(workflow, p, init_image, last_image, reference_media) - - _processed: processing.Processed = scripts_manager.scripts_video.run(p, *args) - processed = processing.process_images(p) - - sd_models.offload_ondemand(shared.sd_model, reason='finish', force=True) # force offload all loaded modules to cpu - devices.torch_gc(force=True) # free gpu memory before saving video # init vars pixels = None @@ -141,65 +105,109 @@ def generate(task_id, _ui_state, video_file = None aac_sample_rate = 32000 - audio = getattr(processed, 'audio', None) if audio_enable else None - if audio is not None: - audio = audio[0].float().cpu() if audio.ndim == 3 else audio.float().cpu() - aac_sample_rate = getattr(shared.sd_model, 'audio_sampling_rate', 32000) + try: + workflow = load_model(model) # override workflow based on loaded model + if not workflow: + progress.finish_task(task_id) + log.error('Video: model not loaded') + return None, 'Model not loaded' + p = processing.StableDiffusionProcessingVideo( + sd_model=shared.sd_model, + video_engine=engine, + video_model=model, + prompt=prompt, + styles=styles, + seed=int(seed) if seed is not None else -1, + steps=int(steps), + width=width, + height=height, + frames=frames, + do_not_save_grid=True, + do_not_save_samples=not mp4_frames, + outpath_samples=paths.resolve_output_path(shared.opts.outdir_samples, shared.opts.outdir_video), + ops=['video'], + ) + video_minimax.apply_overrides(p, shared.sd_model, still=False, audio=audio_enable) + video_minimax.set_sampler_shift(shared.sd_model, video_shift=video_shift, audio_shift=audio_shift) + log.debug(f'Video: engine="{engine}" model="{model}" workflow={workflow} cls={shared.sd_model.__class__.__name__} shift={video_shift}:{audio_shift} kwargs={p.task_args}') + processing.fix_seed(p) + p.ops.append('video') + p.scripts = scripts_manager.scripts_video + p.script_args = args - images = getattr(processed, 'images', []) - if isinstance(images, list): - pixels = video_save.images_to_tensor(images) - elif isinstance(images, np.ndarray): - pixels = video_save.numpy_to_tensor(images) - else: - log.error(f'Video: images={images} type={type(images)} unsupported') + prepare_inputs(workflow, p, init_image, last_image, reference_media) - if pixels is None: - return None, "MiniMax: No frames generated" + _processed: processing.Processed = scripts_manager.scripts_video.run(p, *args) + processed = processing.process_images(p) - if mp4_interpolate > 0: - p.video_interpolate = mp4_interpolate - from modules.processing_video import apply_video_interpolation - # pixels is 5-D (N,C,T,H,W) in [-1,1]; RIFE needs 4-D (T,C,H,W) in [0,1] - x = pixels.squeeze(0).permute(1, 0, 2, 3) - x = (x.clamp(-1., 1.) + 1.0) * 0.5 - x = apply_video_interpolation(p, x, count=mp4_interpolate) # sets p.video_interpolated otherwise main save_video would do it also - x = x * 2.0 - 1.0 - pixels = x.permute(1, 0, 2, 3).unsqueeze(0) + sd_models.offload_ondemand(shared.sd_model, reason='finish', force=True) # force offload all loaded modules to cpu + devices.torch_gc(force=True) # free gpu memory before saving video - save_fps = mp4_fps * processing_video.interpolation_factor(p) - num_frames, video_file, _thumb = video_save.save_video( - p=p, - pixels=pixels, - audio=audio, - mp4_fps=save_fps, - mp4_codec=mp4_codec, - mp4_opt=mp4_opt, - mp4_ext=mp4_ext, - mp4_sf=mp4_sf, - mp4_video=mp4_video, - mp4_frames=mp4_frames, - mp4_thumb=mp4_thumb, - mp4_interpolate=mp4_interpolate, - aac_sample_rate=aac_sample_rate, - metadata={}, - ) - _n, _c, _t, h, w = pixels.shape - del pixels - if audio is not None: - del audio + audio = getattr(processed, 'audio', None) if audio_enable else None + if audio is not None: + audio = audio[0].float().cpu() if audio.ndim == 3 else audio.float().cpu() + aac_sample_rate = getattr(shared.sd_model, 'audio_sampling_rate', 32000) + + images = getattr(processed, 'images', []) + if isinstance(images, list): + pixels = video_save.images_to_tensor(images) + elif isinstance(images, np.ndarray): + pixels = video_save.numpy_to_tensor(images) + else: + log.error(f'Video: images={images} type={type(images)} unsupported') + + if pixels is None: + return None, "MiniMax: No frames generated" + + if mp4_interpolate > 0: + p.video_interpolate = mp4_interpolate + from modules.processing_video import apply_video_interpolation + # pixels is 5-D (N,C,T,H,W) in [-1,1]; RIFE needs 4-D (T,C,H,W) in [0,1] + x = pixels.squeeze(0).permute(1, 0, 2, 3) + x = (x.clamp(-1., 1.) + 1.0) * 0.5 + x = apply_video_interpolation(p, x, count=mp4_interpolate) # sets p.video_interpolated otherwise main save_video would do it also + x = x * 2.0 - 1.0 + pixels = x.permute(1, 0, 2, 3).unsqueeze(0) + + save_fps = mp4_fps * processing_video.interpolation_factor(p) + num_frames, video_file, _thumb = video_save.save_video( + p=p, + pixels=pixels, + audio=audio, + mp4_fps=save_fps, + mp4_codec=mp4_codec, + mp4_opt=mp4_opt, + mp4_ext=mp4_ext, + mp4_sf=mp4_sf, + mp4_video=mp4_video, + mp4_frames=mp4_frames, + mp4_thumb=mp4_thumb, + mp4_interpolate=mp4_interpolate, + aac_sample_rate=aac_sample_rate, + metadata={}, + ) + _n, _c, _t, h, w = pixels.shape + del pixels + if audio is not None: + del audio + + except Exception as e: + log.error(f'Video: engine="{engine}" model="{model}" workflow={workflow} {e}') + errors.display(e, 'Video') + finally: + jobid = getattr(shared.sd_model, 'sdnext_phaseid', None) # previous jobid if any + shared.state.end(jobid) # clear the previous job if exists + progress.finish_task(task_id) + p.close() t1 = time.time() - progress.finish_task(task_id) - p.close() - resolution = f'{w}x{h}' if num_frames > 0 else None summary = timer.process.summary(min_time=0.25, total=False).replace('=', ' ') memory = shared.mem_mon.summary() total_time = max(t1 - t0, 1e-6) fps = f'{num_frames/total_time:.2f}' its = f'{(steps)/total_time:.3f}' - log.info(f'Processed: fn="{video_file}" frames={num_frames} fps={fps} its={its} resolution={resolution} time={total_time:.2f} timers={timer.process.dct()} memory={memstats.memory_stats()}') + log.info(f'Processed: fn="{video_file}" frames={num_frames} fps={fps} its={its} resolution={resolution} time={total_time:.2f} timers={timer.process.dct(no_total=True)} memory={memstats.memory_stats()}') - ui_text = f'MiniMax: Generation completed | File {video_file} | Frames {num_frames} | Resolution {resolution} | f/s {fps} | it/s {its} ' + f"

{summary} {memory}

" + ui_text = f'Video | File {video_file} | Frames {num_frames} | Resolution {resolution} | f/s {fps} | it/s {its} ' + f"

{summary} {memory}

" return video_file, ui_text diff --git a/modules/progress.py b/modules/progress.py index 890ead043..409c2d69f 100644 --- a/modules/progress.py +++ b/modules/progress.py @@ -20,6 +20,7 @@ def start_task(id_task): global current_task # pylint: disable=global-statement current_task = id_task pending_tasks.pop(id_task, None) + log.debug(f'State: start id={id_task} pending={len(pending_tasks)} finished={len(finished_tasks)}') def record_results(id_task, res): @@ -30,10 +31,12 @@ def record_results(id_task, res): def finish_task(id_task): global current_task # pylint: disable=global-statement + log.debug(f'State: end id={id_task}') if current_task == id_task: current_task = None - finished_tasks.append(id_task) - if len(finished_tasks) > 16: + if id_task not in finished_tasks: + finished_tasks.append(id_task) + if len(finished_tasks) > 1024*1024: finished_tasks.pop(0)