sd: generation progress fixes (#2391)

* sd: generation progress fixes

The preview callback is not called if preview images are not enabled,
so when a preview image wasn't requested, the step count wouldn't be
updated. So move the update to the progress callback. Additionally,
adjust the total step count when the progress call reports a lower
total (e.g. for img2img).

Also remove the preview reset from inside the callback, since it
often caused a preview miss, depending on when the next preview
request arrived.

* sd: fix image preview behavior for VAE encoding / tiling

The progress callback is also called for VAE encoding and decoding,
receiving the number of tiles as step count, so there is no simple
way to detect the diffusion beginning. So we set up the first preview
callback to detect it, and transition to the decoding phase when
we reach the last step.
This commit is contained in:
Wagner Bruna 2026-08-11 10:03:11 -03:00 committed by GitHub
parent d135a116da
commit c98c00f9ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -293,19 +293,46 @@ std::string load_gpt_oss_vocab_json()
static void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy, void* data);
// 0 disable, 1 initial, 2 denoised
// the first noisy call is used to detect the inference phase
static void set_preview_images(int enable) {
bool denoised = (enable == 2);
bool noisy = (enable == 1);
sd_set_preview_callback(step_callback, PREVIEW_PROJ, 1, denoised, noisy, nullptr);
}
static inline double get_time_delta(const std::chrono::steady_clock::time_point& start) {
auto now = std::chrono::steady_clock::now();
return std::chrono::duration<double>(now - start).count();
}
static void progress_callback(int step, int steps, float time, void* data)
{
(void) data;
bool enable_preview = false;
const char* phase = "Encoding";
{
std::lock_guard<std::mutex> lock(geninfo.mux);
if (geninfo.preview_requested && !geninfo.preview_enabled) {
geninfo.preview_enabled = true;
enable_preview = true;
set_preview_images(2);
}
}
if (enable_preview) {
sd_set_preview_callback(step_callback, PREVIEW_PROJ, 1, true, false, nullptr);
/* progress_callback is also called for model loading and VAE encoding,
so ignore the step count unless step_callback signals we are diffusing */
if (geninfo.gendata.status == 2) {
/* adjust the max step count (may change for img2img) */
geninfo.steps = steps;
if (step != geninfo.gendata.step) {
geninfo.gendata.step = step;
geninfo.gendata.preview = "";
}
if (step == steps) {
geninfo.gendata.status = 3;
}
phase = "Generating image";
} else if (geninfo.gendata.status == 3) {
phase = "Decoding";
}
/* let the terminal report tiling progress */
}
if(sd_is_quiet || step == 0) return;
@ -319,7 +346,7 @@ static void progress_callback(int step, int steps, float time, void* data)
{
printf("\n");
}
printf("\rGenerating image: %d/%d steps, %.2f %s\033[K%s", step, steps, speed, unit, (step == steps || sddebugmode==1) ? "\n" : "");
printf("\r%s: %d/%d steps, %.2f %s\033[K%s", phase, step, steps, speed, unit, (step == steps || sddebugmode==1) ? "\n" : "");
fflush(stdout);
}
@ -592,7 +619,7 @@ bool sdtype_load_model(const sd_load_model_inputs inputs) {
}
}
sd_set_preview_callback(step_callback, PREVIEW_PROJ, 1, false, false, nullptr);
set_preview_images(0);
sd_set_progress_callback(progress_callback, nullptr);
@ -1049,7 +1076,7 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
geninfo.preview_requested = false;
geninfo.preview_enabled = false;
}
sd_set_preview_callback(step_callback, PREVIEW_PROJ, 1, false, false, nullptr);
set_preview_images(0);
}
} cleanup_info_on_exit;
@ -1066,8 +1093,8 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
geninfo.preview_enabled = false;
geninfo.gendata = {};
geninfo.gendata.status = 1;
set_preview_images(1);
}
sd_set_preview_callback(step_callback, PREVIEW_PROJ, 1, false, false, nullptr);
sd_image_t * results = nullptr;
int generated_num_results = 0;
@ -1420,11 +1447,6 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
sd_audio_t* generated_audio = nullptr;
sd_audio_t input_audio = {0, 0, 0, nullptr};
{
std::lock_guard<std::mutex> lock(geninfo.mux);
geninfo.steps = inputs.sample_steps;
}
if(is_vid_model)
{
std::vector<sd_image_t> control_frames; //empty for now
@ -1747,50 +1769,46 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
return sd_generation.outputs(1);
}
static inline double get_time_delta(const std::chrono::steady_clock::time_point& start) {
auto now = std::chrono::steady_clock::now();
return std::chrono::duration<double>(now - start).count();
}
static void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy, void* data)
{
{
std::lock_guard<std::mutex> lock(geninfo.mux);
if (!geninfo.preview_requested) {
return;
}
geninfo.preview_requested = false;
geninfo.preview_enabled = false;
}
sd_set_preview_callback(step_callback, PREVIEW_PROJ, 1, false, false, nullptr);
gendata_st gendata;
gendata.status = 2;
if (frame_count == 1) {
gendata.preview = raw_image_to_png_base64(*image);
} else {
uint8_t * out_data = nullptr;
size_t out_len = 0;
if (create_gif_buf_from_sd_images_msf(image, frame_count, 16, &out_data,&out_len) == 0 && out_data && out_len > 0) {
gendata.preview = kcpp_base64_encode(out_data, out_len);
}
if (out_data) {
free(out_data);
std::string preview;
if (image != nullptr) {
if (frame_count == 1) {
preview = raw_image_to_png_base64(*image);
} else {
uint8_t * out_data = nullptr;
size_t out_len = 0;
if (create_gif_buf_from_sd_images_msf(image, frame_count, 16, &out_data,&out_len) == 0 && out_data && out_len > 0) {
preview = kcpp_base64_encode(out_data, out_len);
}
if (out_data) {
free(out_data);
}
}
}
gendata.step = step;
gendata.step_time = get_time_delta(geninfo.start_time);
double step_time = get_time_delta(geninfo.start_time);
std::lock_guard<std::mutex> lock(geninfo.mux);
if (step == geninfo.steps)
gendata.status = 3;
geninfo.gendata = gendata;
if (geninfo.gendata.status <= 1) {
geninfo.gendata.status = 2;
if (geninfo.preview_requested && !geninfo.preview_enabled) {
geninfo.preview_enabled = true;
}
if (geninfo.preview_enabled) {
set_preview_images(2);
} else {
set_preview_images(0);
}
}
geninfo.gendata.step = step;
geninfo.gendata.step_time = step_time;
geninfo.gendata.preview = preview;
}
void sdtype_request_ongoing_generation_preview()
{
std::lock_guard<std::mutex> lock(geninfo.mux);
if (geninfo.gendata.status != 0) {
if (geninfo.gendata.status != 0 && !geninfo.preview_requested) {
geninfo.preview_requested = true;
}
}
@ -1902,19 +1920,23 @@ sd_info_outputs sdtype_get_ongoing_generation_info()
}
nlohmann::json j;
j["steps"] = steps;
j["elapsed_time"] = elapsed_time;
j["step"] = gendata.step;
j["step_time"] = gendata.step_time;
if (gendata.status == 1)
j["status"] = "conditioning";
else if (gendata.status == 2)
j["status"] = "diffusing";
else if (gendata.status == 3)
j["status"] = "decoding";
else
if (gendata.status == 0) {
j["status"] = "idle";
j["preview"] = gendata.preview;
} else {
if (gendata.status == 1)
j["status"] = "conditioning";
else if (gendata.status == 2)
j["status"] = "diffusing";
else if (gendata.status == 3)
j["status"] = "decoding";
else
j["status"] = "UNKNOWN";
j["steps"] = steps;
j["elapsed_time"] = elapsed_time;
j["step"] = gendata.step;
j["step_time"] = gendata.step_time;
j["preview"] = gendata.preview;
}
static thread_local std::string recent_info;
recent_info = j.dump();