mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 09:14:35 +02:00
+12
-3
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user