Key the auto step-cache on the pipe's default strength when the request omits it
The auto FBCache policy keyed on the full step count whenever strength was omitted, but the loader only passes the strength kwarg when it is set, so an img2img/inpaint pipe then runs its OWN signature default (< 1, e.g. FluxImg2ImgPipeline's 0.6). FBCache would engage on the full 28 steps while the pipe actually denoises ~16, degrading the image on exactly the short trajectory the policy exists to keep uncached. Thread the pipe's signature default into the policy via a new effective_request_strength helper (unit-tested), so the effective denoise count matches what the pipe runs.
This commit is contained in:
parent
6e5d11a0a7
commit
b6b507c48b
3 changed files with 52 additions and 8 deletions
|
|
@ -79,6 +79,7 @@ from .diffusion_cache import (
|
|||
TC_FBCACHE,
|
||||
apply_step_cache,
|
||||
effective_denoise_steps,
|
||||
effective_request_strength,
|
||||
maybe_toggle_step_cache,
|
||||
normalize_transformer_cache,
|
||||
)
|
||||
|
|
@ -2281,14 +2282,15 @@ class DiffusionBackend:
|
|||
# trajectory the policy keeps uncached. Only fold in `strength` when it
|
||||
# is ACTUALLY applied to the pipe (same gate as the kwarg below), so a
|
||||
# stray strength on a txt2img request never shortens the count.
|
||||
strength_applied = (
|
||||
strength
|
||||
if (
|
||||
strength is not None
|
||||
and init_pil is not None
|
||||
and "strength" in call_params
|
||||
)
|
||||
else None
|
||||
# The pipe denoises `steps * strength`. When the request omits strength the
|
||||
# kwarg above is NOT passed, so the pipe runs its OWN signature default (< 1 for
|
||||
# every img2img/inpaint pipeline here, e.g. 0.6) -- still a short trajectory the
|
||||
# policy must key on, or FBCache engages on a fraction of the advertised steps.
|
||||
strength_applied = effective_request_strength(
|
||||
strength,
|
||||
init_pil is not None,
|
||||
"strength" in call_params,
|
||||
call_params["strength"].default if "strength" in call_params else None,
|
||||
)
|
||||
denoise_steps = effective_denoise_steps(steps, strength_applied)
|
||||
toggled = maybe_toggle_step_cache(
|
||||
|
|
|
|||
|
|
@ -144,6 +144,28 @@ def effective_denoise_steps(steps: int, strength: Optional[float]) -> int:
|
|||
return max(1, min(int(s * float(strength)), s))
|
||||
|
||||
|
||||
def effective_request_strength(
|
||||
request_strength: Optional[float],
|
||||
has_init_image: bool,
|
||||
pipe_accepts_strength: bool,
|
||||
pipe_default_strength: Any,
|
||||
) -> Optional[float]:
|
||||
"""The strength the pipe will ACTUALLY apply, for keying the auto step-cache policy.
|
||||
|
||||
Only image-conditioned pipelines that take ``strength`` apply it (txt2img / a pipe without
|
||||
the kwarg run the full trajectory -> None). When the request omits ``strength`` the loader
|
||||
does NOT pass the kwarg, so the pipe runs its OWN signature default (< 1 for every img2img /
|
||||
inpaint pipeline here, e.g. 0.6); the policy must key on that default, not the full step
|
||||
count, or FBCache engages on a fraction of the advertised steps. A non-numeric default
|
||||
(``inspect.Parameter.empty``) falls back to the full count (None).
|
||||
"""
|
||||
if not (has_init_image and pipe_accepts_strength):
|
||||
return None
|
||||
if request_strength is not None:
|
||||
return request_strength
|
||||
return pipe_default_strength if isinstance(pipe_default_strength, (int, float)) else None
|
||||
|
||||
|
||||
def maybe_toggle_step_cache(
|
||||
pipe: Any,
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ from core.inference.diffusion_cache import ( # noqa: E402
|
|||
FBCACHE_MIN_STEPS,
|
||||
TC_AUTO,
|
||||
effective_denoise_steps,
|
||||
effective_request_strength,
|
||||
maybe_toggle_step_cache,
|
||||
)
|
||||
|
||||
|
|
@ -200,6 +201,25 @@ def test_effective_steps_low_strength_shrinks_below_the_bar():
|
|||
assert eff < FBCACHE_MIN_STEPS
|
||||
|
||||
|
||||
def test_effective_request_strength_uses_pipe_default_when_omitted():
|
||||
import inspect
|
||||
|
||||
# txt2img (no init image) or a pipe without the strength kwarg -> full trajectory (None).
|
||||
assert effective_request_strength(None, False, True, 0.6) is None
|
||||
assert effective_request_strength(0.5, True, False, None) is None
|
||||
# img2img with an explicit strength -> that value.
|
||||
assert effective_request_strength(0.2, True, True, 0.6) == 0.2
|
||||
# img2img with an OMITTED strength -> the pipe's own signature default (< 1), so the auto
|
||||
# policy keys on the real (short) trajectory, not the full step count. This is the fix:
|
||||
# int(28 * 0.6) = 16 real steps, not 28.
|
||||
s = effective_request_strength(None, True, True, 0.6)
|
||||
assert s == 0.6
|
||||
assert effective_denoise_steps(28, s) == 16
|
||||
# A non-numeric signature default (inspect.Parameter.empty) falls back to the full count.
|
||||
assert effective_request_strength(None, True, True, inspect.Parameter.empty) is None
|
||||
assert effective_request_strength(None, True, True, None) is None
|
||||
|
||||
|
||||
def test_effective_steps_matches_diffusers_get_timesteps():
|
||||
# Mirror diffusers exactly: it denoises init_timestep = min(int(num_inference_steps *
|
||||
# strength), num_inference_steps) steps (the product is floored, not rounded).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue