- The exit overlay says which step of shutdown is running and what it waits on ("Stopping the OCR
scan: finishing the text lines it is reading.", the import, the viewer, the models), and how long
that step has taken once it passes 2 s. It said "Saving..." for all of it.
- OCR stops mid-batch: ocr_process takes a cancel flag (Ocr_Engine.cancel), checked by its workers
before each piece of work; a scan sets it to its own cancel. A cut-short batch stores nothing and
counts no failures. Closing during a scan took minutes when a batch held large images; now well
under a second.
- Once shutdown starts, the main thread stops running the app's frames (the viewer, search, import,
OCR, Stats) and draws only the overlay: background_frame read the search thread group while the
shutdown thread was tearing it down, an access violation on exit.
93 lines
4.0 KiB
Plaintext
93 lines
4.0 KiB
Plaintext
// The app's own UI: the capture viewer (app/viewer.jai). Everything else in src/ is the shell
|
|
// around it: the window, the frame loop, the tray, hotkeys, settings, themes, logging and the
|
|
// debug panels.
|
|
|
|
APP_MAIN_WINDOW_LABEL :: "Main";
|
|
|
|
// Once, after ImGui, the fonts, the theme and the settings are up and before the first frame.
|
|
app_ui_startup :: () {
|
|
viewer_startup();
|
|
import_startup();
|
|
ocr_ui_startup();
|
|
labels_ui_startup();
|
|
}
|
|
|
|
// Every frame, before input is handed to ImGui. Poll background work here.
|
|
app_ui_frame_start :: () {
|
|
}
|
|
|
|
// Once, on the shutdown thread after the window is asked to close (exit_shutdown.jai): save and
|
|
// stop background work here. Must not touch ImGui, since the main thread is still drawing frames
|
|
// (the overlay; app_main_window_ui stops running the app's frames once shutdown starts).
|
|
app_ui_shutdown :: () {
|
|
app_shutdown_step(.IMPORT);
|
|
import_shutdown(); // before the viewer: it hands the viewer what it writes
|
|
app_shutdown_step(.VIEWER);
|
|
viewer_shutdown();
|
|
app_shutdown_step(.OCR);
|
|
ocr_ui_shutdown();
|
|
app_shutdown_step(.LABELS);
|
|
labels_ui_shutdown(); // after the OCR thread and the background workers have stopped
|
|
app_shutdown_step(.STATS);
|
|
stats_ui_shutdown();
|
|
}
|
|
|
|
// Which step of app_ui_shutdown is running, and since when, for the overlay a slow shutdown shows.
|
|
App_Shutdown_Step :: enum s32 { NONE; IMPORT; VIEWER; OCR; LABELS; STATS; }
|
|
app_shutdown: struct {
|
|
step: App_Shutdown_Step;
|
|
step_started_ns: s64; // monotonic
|
|
}
|
|
|
|
app_shutdown_step :: (step: App_Shutdown_Step) {
|
|
atomic_write(*app_shutdown.step_started_ns, to_nanoseconds(current_time_monotonic()));
|
|
atomic_write(*app_shutdown.step, step);
|
|
}
|
|
|
|
// Main thread, while app_ui_shutdown runs: what it is waiting on, as a line or two for the overlay
|
|
// (temp); "" before it has started a step.
|
|
app_ui_shutdown_status :: () -> string {
|
|
step := atomic_read(*app_shutdown.step);
|
|
text: string;
|
|
if step == {
|
|
case .NONE; return "";
|
|
case .IMPORT;
|
|
if importer.phase == .INDEXING text = "Stopping the import: it is indexing the library, and stops after the files it is on.";
|
|
else if importer.running text = "Stopping the import: finishing the files it is on (a large video takes a while to copy).";
|
|
else text = "Stopping the import.";
|
|
case .VIEWER;
|
|
text = "Stopping the thumbnail, catalog and search workers";
|
|
if viewer.open_started text = tprint("%, and writing out % to play", text, viewer.open_name);
|
|
text = tprint("%.", text);
|
|
case .OCR;
|
|
text = "Closing the OCR model.";
|
|
if ocr_ui.thread_busy if ocr_ui.task.kind == {
|
|
case .SCAN; text = "Stopping the OCR scan: finishing the text lines it is reading.";
|
|
case .ERASE; text = "Stopping the erase: finishing the capture it is on.";
|
|
case .READ; text = "Waiting for OCR to finish reading a capture.";
|
|
case .CREATE; text = "Waiting for the OCR model to finish loading.";
|
|
case .LABELS_CREATE; text = "Waiting for the label model to finish loading.";
|
|
case .LABEL_SET; text = "Waiting for the label list to finish.";
|
|
case; text = "Stopping OCR.";
|
|
}
|
|
case .LABELS; text = "Closing the label model.";
|
|
case .STATS; text = "Closing Stats.";
|
|
}
|
|
waited := cast(float64) (to_nanoseconds(current_time_monotonic()) - atomic_read(*app_shutdown.step_started_ns)) / 1_000_000_000.0;
|
|
if waited >= 2 text = tprint("%\n(this step: % s)", text, FormatFloat.{value = waited, trailing_width = 0, zero_removal = .NO});
|
|
return text;
|
|
}
|
|
|
|
app_main_window_ui :: () {
|
|
push_imgui_window(APP_MAIN_WINDOW_LABEL);
|
|
// Once shutdown has started, its thread is stopping the threads these frames hand work to and take
|
|
// it from (the viewer's workers, the search group, the import, OCR), and freeing what they share:
|
|
// running them now would race it. The exit overlay says what it is waiting on.
|
|
if app_exit_shutdown_started() return;
|
|
viewer_ui();
|
|
ocr_ui_frame();
|
|
labels_ui_frame();
|
|
stats_ui_frame();
|
|
import_frame();
|
|
}
|