Spaces:
Running on Zero
Running on Zero
Commit ·
9fcf338
1
Parent(s): 1f6b940
Speed up fast mode: lower resolution cap, skip needless VAE tiling, fewer steps
Browse filesFast mode's 768px (was 1024px) output now stays under the VAE's tiling
threshold, so it decodes in a single pass instead of a tiled decode with
blend overhead that was only ever needed for high-detail's 2048px case.
Also gives fast mode a lower default step count (3 vs 4), following the
existing mode-scoped-default pattern already used for GPU duration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- app.py +3 -4
- dimensions.py +1 -1
- static/mode_toggle.js +13 -7
- templates/app.html +2 -2
- tests/test_dimensions.py +12 -1
app.py
CHANGED
|
@@ -29,7 +29,6 @@ _log_uploader = LogUploader(
|
|
| 29 |
|
| 30 |
MAX_SEED = np.iinfo(np.int32).max
|
| 31 |
LANCZOS = getattr(Image, "Resampling", Image).LANCZOS
|
| 32 |
-
MAX_OUTPUT_DIM = 2048
|
| 33 |
|
| 34 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 35 |
|
|
@@ -71,7 +70,7 @@ torch.backends.cudnn.allow_tf32 = True
|
|
| 71 |
print("[startup] TF32 enabled", flush=True)
|
| 72 |
|
| 73 |
print("[startup] importing dimensions...", flush=True)
|
| 74 |
-
from dimensions import compute_output_dimensions, max_dim_for_mode
|
| 75 |
print("[startup] importing diffusers...", flush=True)
|
| 76 |
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 77 |
from diffusers.models.normalization import RMSNorm
|
|
@@ -190,7 +189,7 @@ pipe = QwenImageEditPlusPipeline.from_pretrained(
|
|
| 190 |
torch_dtype=dtype,
|
| 191 |
)
|
| 192 |
_hb.set()
|
| 193 |
-
pipe.vae.enable_tiling()
|
| 194 |
print(f"[startup] pipeline loaded in {time.perf_counter()-_t1_load:.1f}s", flush=True)
|
| 195 |
|
| 196 |
print("[startup] setting cuDNN SDPA attention processor...", flush=True)
|
|
@@ -624,7 +623,7 @@ with gr.Blocks() as demo:
|
|
| 624 |
seed = gr.Slider(minimum=0, maximum=MAX_SEED, step=1, value=0, elem_id="gradio-seed", elem_classes="hidden-input", container=False)
|
| 625 |
randomize_seed = gr.Checkbox(value=True, elem_id="gradio-randomize", elem_classes="hidden-input", container=False)
|
| 626 |
guidance_scale = gr.Slider(minimum=1.0, maximum=10.0, step=0.1, value=1.0, elem_id="gradio-guidance", elem_classes="hidden-input", container=False)
|
| 627 |
-
steps = gr.Slider(minimum=1, maximum=50, step=1, value=
|
| 628 |
mode = gr.Textbox(value="fast", elem_id="gradio-mode", elem_classes="hidden-input", container=False)
|
| 629 |
gpu_duration = gr.Slider(minimum=10, maximum=120, step=5, value=30, elem_id="gradio-gpu-duration", elem_classes="hidden-input", container=False)
|
| 630 |
result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
|
|
|
|
| 29 |
|
| 30 |
MAX_SEED = np.iinfo(np.int32).max
|
| 31 |
LANCZOS = getattr(Image, "Resampling", Image).LANCZOS
|
|
|
|
| 32 |
|
| 33 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 34 |
|
|
|
|
| 70 |
print("[startup] TF32 enabled", flush=True)
|
| 71 |
|
| 72 |
print("[startup] importing dimensions...", flush=True)
|
| 73 |
+
from dimensions import compute_output_dimensions, max_dim_for_mode, MAX_OUTPUT_DIM_FAST
|
| 74 |
print("[startup] importing diffusers...", flush=True)
|
| 75 |
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 76 |
from diffusers.models.normalization import RMSNorm
|
|
|
|
| 189 |
torch_dtype=dtype,
|
| 190 |
)
|
| 191 |
_hb.set()
|
| 192 |
+
pipe.vae.enable_tiling(tile_sample_min_height=MAX_OUTPUT_DIM_FAST, tile_sample_min_width=MAX_OUTPUT_DIM_FAST)
|
| 193 |
print(f"[startup] pipeline loaded in {time.perf_counter()-_t1_load:.1f}s", flush=True)
|
| 194 |
|
| 195 |
print("[startup] setting cuDNN SDPA attention processor...", flush=True)
|
|
|
|
| 623 |
seed = gr.Slider(minimum=0, maximum=MAX_SEED, step=1, value=0, elem_id="gradio-seed", elem_classes="hidden-input", container=False)
|
| 624 |
randomize_seed = gr.Checkbox(value=True, elem_id="gradio-randomize", elem_classes="hidden-input", container=False)
|
| 625 |
guidance_scale = gr.Slider(minimum=1.0, maximum=10.0, step=0.1, value=1.0, elem_id="gradio-guidance", elem_classes="hidden-input", container=False)
|
| 626 |
+
steps = gr.Slider(minimum=1, maximum=50, step=1, value=3, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
|
| 627 |
mode = gr.Textbox(value="fast", elem_id="gradio-mode", elem_classes="hidden-input", container=False)
|
| 628 |
gpu_duration = gr.Slider(minimum=10, maximum=120, step=5, value=30, elem_id="gradio-gpu-duration", elem_classes="hidden-input", container=False)
|
| 629 |
result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
|
dimensions.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
MAX_OUTPUT_DIM = 2048
|
| 2 |
-
MAX_OUTPUT_DIM_FAST =
|
| 3 |
|
| 4 |
|
| 5 |
def max_dim_for_mode(mode): return MAX_OUTPUT_DIM_FAST if mode == "fast" else MAX_OUTPUT_DIM
|
|
|
|
| 1 |
MAX_OUTPUT_DIM = 2048
|
| 2 |
+
MAX_OUTPUT_DIM_FAST = 768
|
| 3 |
|
| 4 |
|
| 5 |
def max_dim_for_mode(mode): return MAX_OUTPUT_DIM_FAST if mode == "fast" else MAX_OUTPUT_DIM
|
static/mode_toggle.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
| 1 |
() => {
|
| 2 |
window.__selectedMode = 'fast';
|
| 3 |
var MODE_GPU_DURATION = { fast: 30, high_detail: 60 };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
window.__setMode = function(m) {
|
| 5 |
window.__selectedMode = m;
|
| 6 |
var fast = document.getElementById('mode-btn-fast');
|
| 7 |
var hd = document.getElementById('mode-btn-hd');
|
| 8 |
if (fast) fast.classList.toggle('mode-btn-active', m === 'fast');
|
| 9 |
if (hd) hd.classList.toggle('mode-btn-active', m === 'high_detail');
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
var sl = document.getElementById('custom-gpu-duration');
|
| 13 |
-
var vl = document.getElementById('custom-gpu-duration-val');
|
| 14 |
-
if (sl) { sl.value = dur; sl.dispatchEvent(new Event('input', {bubbles: true})); }
|
| 15 |
-
if (vl) vl.textContent = dur;
|
| 16 |
-
}
|
| 17 |
};
|
| 18 |
}
|
|
|
|
| 1 |
() => {
|
| 2 |
window.__selectedMode = 'fast';
|
| 3 |
var MODE_GPU_DURATION = { fast: 30, high_detail: 60 };
|
| 4 |
+
var MODE_STEPS = { fast: 3, high_detail: 4 };
|
| 5 |
+
|
| 6 |
+
function applyModeSliderDefault(sliderId, valueMap, mode) {
|
| 7 |
+
var val = valueMap[mode];
|
| 8 |
+
if (val === undefined) return;
|
| 9 |
+
var sl = document.getElementById(sliderId);
|
| 10 |
+
var vl = document.getElementById(sliderId + '-val');
|
| 11 |
+
if (sl) { sl.value = val; sl.dispatchEvent(new Event('input', {bubbles: true})); }
|
| 12 |
+
if (vl) vl.textContent = val;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
window.__setMode = function(m) {
|
| 16 |
window.__selectedMode = m;
|
| 17 |
var fast = document.getElementById('mode-btn-fast');
|
| 18 |
var hd = document.getElementById('mode-btn-hd');
|
| 19 |
if (fast) fast.classList.toggle('mode-btn-active', m === 'fast');
|
| 20 |
if (hd) hd.classList.toggle('mode-btn-active', m === 'high_detail');
|
| 21 |
+
applyModeSliderDefault('custom-gpu-duration', MODE_GPU_DURATION, m);
|
| 22 |
+
applyModeSliderDefault('custom-steps', MODE_STEPS, m);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
};
|
| 24 |
}
|
templates/app.html
CHANGED
|
@@ -139,8 +139,8 @@
|
|
| 139 |
</div>
|
| 140 |
<div class="slider-row">
|
| 141 |
<label>Steps</label>
|
| 142 |
-
<input type="range" id="custom-steps" min="1" max="50" step="1" value="
|
| 143 |
-
<span class="slider-val" id="custom-steps-val">
|
| 144 |
</div>
|
| 145 |
</div>
|
| 146 |
</div>
|
|
|
|
| 139 |
</div>
|
| 140 |
<div class="slider-row">
|
| 141 |
<label>Steps</label>
|
| 142 |
+
<input type="range" id="custom-steps" min="1" max="50" step="1" value="3">
|
| 143 |
+
<span class="slider-val" id="custom-steps-val">3</span>
|
| 144 |
</div>
|
| 145 |
</div>
|
| 146 |
</div>
|
tests/test_dimensions.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import pytest
|
| 2 |
-
from dimensions import compute_output_dimensions, MAX_OUTPUT_DIM
|
| 3 |
|
| 4 |
|
| 5 |
def aspect_ratio_error(w_in, h_in, w_out, h_out):
|
|
@@ -97,3 +97,14 @@ def test_custom_max_dim():
|
|
| 97 |
nw, nh = compute_output_dimensions(1920, 1080, max_dim=1024)
|
| 98 |
assert nw == 1024
|
| 99 |
assert nh % 8 == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import pytest
|
| 2 |
+
from dimensions import compute_output_dimensions, max_dim_for_mode, MAX_OUTPUT_DIM, MAX_OUTPUT_DIM_FAST
|
| 3 |
|
| 4 |
|
| 5 |
def aspect_ratio_error(w_in, h_in, w_out, h_out):
|
|
|
|
| 97 |
nw, nh = compute_output_dimensions(1920, 1080, max_dim=1024)
|
| 98 |
assert nw == 1024
|
| 99 |
assert nh % 8 == 0
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
# --- mode -> max dimension mapping ---
|
| 103 |
+
|
| 104 |
+
def test_fast_mode_uses_fast_max_dim():
|
| 105 |
+
assert max_dim_for_mode("fast") == MAX_OUTPUT_DIM_FAST
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
@pytest.mark.parametrize("mode", ["high_detail", "anything_else", None])
|
| 109 |
+
def test_non_fast_modes_use_default_max_dim(mode):
|
| 110 |
+
assert max_dim_for_mode(mode) == MAX_OUTPUT_DIM
|