diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e347ba1a..45081dc25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Change Log for SD.Next -## Update for 2026-08-11 +## TODO + +- video API: video, text2image, image2image +- MiniMax-H3 +- LTX-2.5 +- Group offloading in 16gb + +## Update for 2026-08-13 - **Detailer**: Pretty much *detailer.next* :) Detailer detection models were traditionally *YOLO* models, but now we can also use: @@ -18,10 +25,11 @@ - [SDNQ](https://github.com/Disty0/sdnq) is now a separate package and no longer part of sdnext repo installed and used internally by sd.next, but also supported by diffusers natively - **Server** - - update handlers for all authenticated workflows - nunchaku-lite support for `torch==2.13` + - update handlers for all authenticated workflows + - update handlers for all hf-based progress bars - log long torch autotune operations - - utilize torch.accelerator + - utilize `torch.accelerator` where available - add `SD_DIFFUSERS_DEBUG` and `SD_TRANSFORMERS_DEBUG` env variables to trace diffusers and transformers internal operations - **Removed** - remove DirectML support @@ -33,6 +41,7 @@ - improve handling of hf auth - improve pipeline detection for non-cached models - cleanup alt offload codepaths + - hf progress bars ## Update for 2026-08-07 diff --git a/modules/framepack/framepack_worker.py b/modules/framepack/framepack_worker.py index 0504de761..5a0388336 100644 --- a/modules/framepack/framepack_worker.py +++ b/modules/framepack/framepack_worker.py @@ -378,5 +378,5 @@ def worker( sd_models.apply_balanced_offload(shared.sd_model) stream.output_queue.push(('end', None)) t1 = time.time() - log.info(f'Processed: frames={total_generated_frames} fps={total_generated_frames/(t1-t0):.2f} its={(shared.state.sampling_step)/(t1-t0):.2f} time={t1-t0:.2f} timers={timer.process.dct()} memory={memstats.memory_stats()}') + log.info(f'Processed: frames={total_generated_frames} fps={total_generated_frames/(t1-t0):.2f} its={(shared.state.sampling_step)/(t1-t0):.3f} time={t1-t0:.2f} timers={timer.process.dct()} memory={memstats.memory_stats()}') shared.state.end(videojob) diff --git a/modules/ltx/ltx_process.py b/modules/ltx/ltx_process.py index 8c4634407..39abe0643 100644 --- a/modules/ltx/ltx_process.py +++ b/modules/ltx/ltx_process.py @@ -670,7 +670,7 @@ def run_ltx(task_id, memory = shared.mem_mon.summary() total_time = max(t_end - t0, 1e-6) fps = f'{num_frames/total_time:.2f}' - its = f'{(steps)/total_time:.2f}' + its = f'{(steps)/total_time:.3f}' shared.state.end(videojob) progress.finish_task(task_id) diff --git a/modules/modular_load.py b/modules/modular_load.py index 76bc28636..dc1717b52 100644 --- a/modules/modular_load.py +++ b/modules/modular_load.py @@ -29,12 +29,12 @@ def install_state_hook(pipe): runner_log.addFilter(InterruptLogFilter()) def set_phase(phase: str, module: torch.nn.Module | None = None): - # every stage runs inside one pipeline call, so the forward hooks are the only place the current stage is visible; state.begin clears the label per job + # every stage runs inside one pipeline call, so the forward hooks are the only place the current stage is visible if getattr(pipe, 'sdnext_phase', None) != phase: pipe.sdnext_phase = phase - jobid = getattr(pipe, 'sdnext_phaseid', None) - shared.state.end(jobid) - pipe.sdnext_phaseid = shared.state.begin(phase) + jobid = getattr(pipe, 'sdnext_phaseid', None) # previous jobid if any + shared.state.end(jobid) # clear the previous job if exists + pipe.sdnext_phaseid = shared.state.begin(phase) # start a new job for the current phase log.debug(f'Pipeline: phase={phase} cls={pipe.__class__.__name__} module={module.__class__.__name__ if module is not None else None}') def _pre_transformer_hook(module, args): # pylint: disable=unused-argument @@ -179,7 +179,7 @@ def load_modular_pipe(repo_cls, repo: str, workflow: str | None = None, revision preloaded = preload_components(pipe, workflow, load_config=load_config) if preloaded: pipe.update_components(**preloaded) # registered before the rest, which load_components then skips - log.debug(f'Load modular: preloaded={list(preloaded)}') + log.debug(f'Load modular: cls={pipe.__class__.__name__} preloaded={list(preloaded)}') pipe.load_components( workflow=workflow, dtype=devices.dtype, diff --git a/modules/processing.py b/modules/processing.py index 2134a8ac5..b5cd149e7 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -590,7 +590,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: p.ops = list(set(p.ops)) if not p.disable_extra_networks: - log.info(f'Processed: images={len(output_images)} its={(p.steps * len(output_images)) / (t1 - t0):.2f} ops={p.ops}') + log.info(f'Processed: images={len(output_images)} its={(p.steps * len(output_images)) / (t1 - t0):.3f} ops={p.ops}') print_stats() if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: diff --git a/modules/processing_args.py b/modules/processing_args.py index d6f427131..d4559b7f6 100644 --- a/modules/processing_args.py +++ b/modules/processing_args.py @@ -19,8 +19,8 @@ debug_log = log.trace if debug_enabled else lambda *args, **kwargs: None disable_pbar = os.environ.get('SD_DISABLE_PBAR', None) is not None -def task_modular_kwargs(p, model): # pylint: disable=unused-argument - # model_cls = model.__class__.__name__ +def task_modular_kwargs(p, model): + model_cls = model.__class__.__name__ task_args = {} p.ops.append('modular') @@ -34,6 +34,15 @@ def task_modular_kwargs(p, model): # pylint: disable=unused-argument if mask_image is not None: task_args['mask_image'] = mask_image + if model_cls in ['MiniMaxH3ModularPipeline'] and task_args.get('image', None) is not None: + if len(task_args.get('image', [])) > 2: + task_args['normalized_references'] = task_args['image'] + task_args.pop('image', None) # remove image, only use normalized_references + elif len(task_args.get('image', [])) > 1: + task_args['last_image'] = task_args['image'][1] + if len(task_args.get('image', [])) > 0: + task_args['image'] = task_args['image'][0] + if debug_enabled: debug_log(f'Process task specific args: {task_args}') return task_args @@ -170,13 +179,15 @@ def task_specific_kwargs(p, model): def get_params(model): + possible = [] if hasattr(model, 'blocks') and hasattr(model.blocks, 'inputs'): # modular pipeline possible = [input_param.name for input_param in model.blocks.inputs] - return possible + ['output'] # __call__ param selecting which state values to return, not a block input + possible += ['output'] # __call__ param selecting which state values to return, not a block input else: signature = inspect.signature(type(model).__call__, follow_wrapped=True) possible = list(signature.parameters) - return possible + possible = [p for p in possible if p not in ['self', 'kwargs', None]] + return possible def get_defaults(model, kwargs): @@ -241,8 +252,7 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:l possible = get_params(model) - if debug_enabled: - debug_log(f'Process pipeline possible: {possible}') + log.debug(f'Pipeline: cls={cls} possible={possible}') steps = kwargs.get("num_inference_steps", None) or len(getattr(p, 'timesteps', ['1'])) clip_skip = kwargs.pop("clip_skip", 1)