From e29d10624cc20dc8dd3441038f4d8fb43e4698be Mon Sep 17 00:00:00 2001 From: Brendan Lackey Date: Sat, 3 May 2025 16:45:29 -0700 Subject: [PATCH 1/6] Add lazy scheduling + use params from denoiser --- scripts/detail_daemon.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/scripts/detail_daemon.py b/scripts/detail_daemon.py index d63d990..148eccf 100644 --- a/scripts/detail_daemon.py +++ b/scripts/detail_daemon.py @@ -8,7 +8,7 @@ matplotlib.use('Agg') import matplotlib.pyplot as plt import modules.scripts as scripts -from modules.script_callbacks import on_cfg_denoiser, remove_callbacks_for_function, on_infotext_pasted +from modules.script_callbacks import CFGDenoiserParams, on_cfg_denoiser, remove_callbacks_for_function, on_infotext_pasted from modules.ui_components import InputAccordion @@ -28,6 +28,11 @@ on_infotext_pasted(parse_infotext) class Script(scripts.Script): + def __init__(self): + super().__init__() + self.schedule_params: dict[str, float] = None + self.schedule = None + def title(self): return "Detail Daemon" @@ -118,10 +123,18 @@ class Script(scripts.Script): if p.sampler_name == "DPM adaptive": tqdm.write(f'\033[33mWARNING:\033[0m Detail Daemon does not work with {p.sampler_name}') return - # Restart can be handled better, later maybe - actual_steps = (p.steps * 2 - 1) if p.sampler_name in ['DPM++ SDE', 'DPM++ 2S a', 'Heun', 'DPM2', 'DPM2 a', 'Restart'] else p.steps - self.schedule = self.make_schedule(actual_steps, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth) + self.schedule_params = { + "start": start, + "end": end, + "bias": bias, + "amount": amount, + "exponent": exponent, + "start_offset": start_offset, + "end_offset": end_offset, + "fade": fade, + "smooth": smooth + } self.mode = mode self.cfg_scale = p.cfg_scale self.batch_size = p.batch_size @@ -133,6 +146,7 @@ class Script(scripts.Script): if hasattr(self, 'callback_added'): remove_callbacks_for_function(self.denoiser_callback) delattr(self, 'callback_added') + self.schedule = None # tqdm.write('\033[90mINFO: Detail Daemon callback removed\033[0m') def before_process_batch(self, p, *args, **kwargs): @@ -141,7 +155,8 @@ class Script(scripts.Script): def postprocess(self, p, processed, *args): if hasattr(self, 'callback_added'): remove_callbacks_for_function(self.denoiser_callback) - delattr(self, 'callback_added') + delattr(self, 'callback_added') + self.schedule = None # tqdm.write('\033[90mINFO: Detail Daemon callback removed\033[0m') def before_hr(self, p, *args): @@ -150,13 +165,20 @@ class Script(scripts.Script): if enabled: tqdm.write(f'\033[33mINFO:\033[0m Detail Daemon does not work during Hires Fix') - def denoiser_callback(self, params): + def denoiser_callback(self, params: CFGDenoiserParams): if self.is_hires: return - idx = params.denoiser.step + + if self.schedule is None: + self.schedule = self.make_schedule( + max(params.total_sampling_steps, params.denoiser.total_steps), + **self.schedule_params, + ) + + idx = max(params.sampling_step, params.denoiser.step) multiplier = self.schedule[idx] * .1 mode = self.mode - if params.sigma.size(0) == 1: + if params.sigma.size(0) == 1 and mode != "both": mode = "both" if idx == 0: tqdm.write(f'\033[33mWARNING:\033[0m Forge does not support `cond` and `uncond` modes, using `both` instead') From 09908f45f4b15f882b9b6114846d3c23e35e8d82 Mon Sep 17 00:00:00 2001 From: Brendan Lackey Date: Sat, 3 May 2025 17:05:02 -0700 Subject: [PATCH 2/6] Add lazy scheduling + use params from denoiser --- scripts/detail_daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/detail_daemon.py b/scripts/detail_daemon.py index 148eccf..f807787 100644 --- a/scripts/detail_daemon.py +++ b/scripts/detail_daemon.py @@ -145,7 +145,7 @@ class Script(scripts.Script): else: if hasattr(self, 'callback_added'): remove_callbacks_for_function(self.denoiser_callback) - delattr(self, 'callback_added') + delattr(self, 'callback_added') self.schedule = None # tqdm.write('\033[90mINFO: Detail Daemon callback removed\033[0m') From 2fe2ec203df2ac957173bb4d0a5a772a7d588ecb Mon Sep 17 00:00:00 2001 From: Brendan Lackey Date: Sat, 3 May 2025 17:06:08 -0700 Subject: [PATCH 3/6] Add lazy scheduling + use params from denoiser --- scripts/detail_daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/detail_daemon.py b/scripts/detail_daemon.py index f807787..148eccf 100644 --- a/scripts/detail_daemon.py +++ b/scripts/detail_daemon.py @@ -145,7 +145,7 @@ class Script(scripts.Script): else: if hasattr(self, 'callback_added'): remove_callbacks_for_function(self.denoiser_callback) - delattr(self, 'callback_added') + delattr(self, 'callback_added') self.schedule = None # tqdm.write('\033[90mINFO: Detail Daemon callback removed\033[0m') From b6c7e57c38d929a9f49ef73ea70b1ce891c90d72 Mon Sep 17 00:00:00 2001 From: Brendan Lackey Date: Sat, 3 May 2025 17:06:41 -0700 Subject: [PATCH 4/6] Remove CFGDenoiserParams type hint --- scripts/detail_daemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/detail_daemon.py b/scripts/detail_daemon.py index 148eccf..4a98fe8 100644 --- a/scripts/detail_daemon.py +++ b/scripts/detail_daemon.py @@ -8,7 +8,7 @@ matplotlib.use('Agg') import matplotlib.pyplot as plt import modules.scripts as scripts -from modules.script_callbacks import CFGDenoiserParams, on_cfg_denoiser, remove_callbacks_for_function, on_infotext_pasted +from modules.script_callbacks import on_cfg_denoiser, remove_callbacks_for_function, on_infotext_pasted from modules.ui_components import InputAccordion @@ -165,7 +165,7 @@ class Script(scripts.Script): if enabled: tqdm.write(f'\033[33mINFO:\033[0m Detail Daemon does not work during Hires Fix') - def denoiser_callback(self, params: CFGDenoiserParams): + def denoiser_callback(self, params): if self.is_hires: return From 140d065804298cec620b8423d45a24a57b5c16c4 Mon Sep 17 00:00:00 2001 From: Brendan Lackey Date: Sat, 3 May 2025 17:43:53 -0700 Subject: [PATCH 5/6] Add HeunPP2 to unsupported list [Forge Only] --- scripts/detail_daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/detail_daemon.py b/scripts/detail_daemon.py index 4a98fe8..a7c23da 100644 --- a/scripts/detail_daemon.py +++ b/scripts/detail_daemon.py @@ -120,7 +120,7 @@ class Script(scripts.Script): smooth = getattr(p, "DD_smooth", smooth) if enabled: - if p.sampler_name == "DPM adaptive": + if p.sampler_name in ["DPM adaptive", "HeunPP2"]: tqdm.write(f'\033[33mWARNING:\033[0m Detail Daemon does not work with {p.sampler_name}') return From f26e303f65abaaac6a3085c4620c2ed1e428dfd2 Mon Sep 17 00:00:00 2001 From: Brendan Lackey Date: Thu, 29 May 2025 16:22:51 -0700 Subject: [PATCH 6/6] Account for 2nd order samplers with integer division --- scripts/detail_daemon.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/detail_daemon.py b/scripts/detail_daemon.py index a7c23da..5666481 100644 --- a/scripts/detail_daemon.py +++ b/scripts/detail_daemon.py @@ -170,10 +170,9 @@ class Script(scripts.Script): return if self.schedule is None: - self.schedule = self.make_schedule( - max(params.total_sampling_steps, params.denoiser.total_steps), - **self.schedule_params, - ) + total_steps = max(params.total_sampling_steps, params.denoiser.total_steps) + corrected_step_count = total_steps - max(total_steps // params.denoiser.steps - 1, 0) + self.schedule = self.make_schedule(corrected_step_count, **self.schedule_params) idx = max(params.sampling_step, params.denoiser.step) multiplier = self.schedule[idx] * .1