Skip to content

Commit 9a2bdc2

Browse files
committed
Probe and expose runtime renderer availability
Add runtime backend probing and reporting: introduce BackendRuntimeInfo and APIs to probe, cache and clear backend availability (refresh_runtime_backend_info, runtime_backend_info, backend_kind_is_available, backend_unavailable_reason). Wire renderer-side probe hooks for Vulkan/Metal/OpenGL and add a safe GLFW init/is_initialized guard so probing can initialize and terminate GLFW as needed. Update CLI (--list-backends), preferences UI and test-engine JSON to show runtime availability and unavailability reasons and disable/ignore unavailable backends. Improve backend resolution to prefer runtime-available builds and emit clearer diagnostics when no backend is usable. Also include robustness fixes to screen-capture code (handle negative coords/full-capture fallback and try front/back read buffers for GL) and small tooling updates to regression scripts to account for runtime availability and window-relative click coordinates.
1 parent 8368b90 commit 9a2bdc2

20 files changed

Lines changed: 673 additions & 168 deletions

src/doc/imiv_dev.rst

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,27 +116,28 @@ The startup sequence in `src/imiv/imiv_app.cpp` is:
116116

117117
1. `imiv_main.cpp` parses CLI arguments into `AppConfig`.
118118
2. `run()` loads persistent app state with `load_persistent_state()`.
119-
3. `run()` resolves the requested backend with
119+
3. `run()` initializes GLFW and refreshes runtime backend availability.
120+
4. `run()` resolves the requested backend with
120121
`requested_backend_for_launch()` and `resolve_backend_request()`.
121-
4. GLFW is initialized and the main window is created.
122-
5. The Dear ImGui context is created and configured:
122+
5. The main window is created for the resolved backend.
123+
6. The Dear ImGui context is created and configured:
123124

124125
* `ImGuiConfigFlags_NavEnableKeyboard`
125126
* `ImGuiConfigFlags_DockingEnable`
126127
* `ImGuiConfigFlags_ViewportsEnable`
127128

128-
6. Fonts and the base application style are loaded.
129-
7. Dear ImGui layout data is loaded from disk with
129+
7. Fonts and the base application style are loaded.
130+
8. Dear ImGui layout data is loaded from disk with
130131
`ImGui::LoadIniSettingsFromDisk()`.
131-
8. The selected renderer backend is initialized:
132+
9. The selected renderer backend is initialized:
132133

133134
* instance/device setup;
134135
* swapchain or drawable setup;
135136
* backend-specific Dear ImGui renderer bootstrap.
136137

137-
9. Optional OCIO preview support is preflighted for the active backend.
138-
10. Startup images are loaded with `load_viewer_image()`.
139-
11. Drag and drop and optional Dear ImGui Test Engine hooks are installed.
138+
10. Optional OCIO preview support is preflighted for the active backend.
139+
11. Startup images are loaded with `load_viewer_image()`.
140+
12. Drag and drop and optional Dear ImGui Test Engine hooks are installed.
140141

141142
Main loop
142143
---------

src/doc/imiv_user.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ image list. For example::
5353
--view "ACES 2.0 - SDR 100 nits (Rec.709)" \
5454
--image-color-space ACEScg image.exr
5555

56-
To see which renderer backends were compiled into the current binary::
56+
To see which renderer backends were compiled into the current binary and which
57+
of those are currently usable on this machine::
5758

5859
imiv --list-backends
5960

@@ -80,13 +81,15 @@ Use :program:`imiv --help` to print the option summary for the current build.
8081

8182
The command-line request takes precedence over the saved backend
8283
preference. If the requested backend was not compiled into the current
83-
binary, :program:`imiv` falls back to the resolved default backend and
84-
prints a message.
84+
binary, or was compiled but is not currently available at runtime,
85+
:program:`imiv` falls back to the resolved default backend and prints a
86+
message.
8587

8688
.. option:: --list-backends
8789

8890
Print the backend support compiled into the current :program:`imiv`
89-
binary, then exit.
91+
binary, including runtime availability and any unavailability reason, then
92+
exit.
9093

9194
.. option:: --display NAME
9295

src/imiv/imiv_app.cpp

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,6 @@ run(const AppConfig& config)
192192
}
193193
const BackendKind requested_backend = requested_backend_for_launch(
194194
run_config, ui_state);
195-
const BackendKind active_backend = resolve_backend_request(
196-
requested_backend);
197-
if (active_backend == BackendKind::Auto) {
198-
print(stderr, "imiv: no renderer backend is compiled into this build\n");
199-
return EXIT_FAILURE;
200-
}
201-
if (requested_backend != BackendKind::Auto
202-
&& requested_backend != active_backend) {
203-
print("imiv: requested backend '{}' is not compiled into this build; "
204-
"using '{}'\n",
205-
backend_cli_name(requested_backend),
206-
backend_runtime_name(active_backend));
207-
}
208195

209196
const bool verbose_logging = run_config.verbose;
210197
const bool verbose_validation_output
@@ -230,6 +217,50 @@ run(const AppConfig& config)
230217
return EXIT_FAILURE;
231218
}
232219

220+
refresh_runtime_backend_info(verbose_logging, startup_error);
221+
222+
const BackendKind active_backend = resolve_backend_request(
223+
requested_backend);
224+
if (active_backend == BackendKind::Auto) {
225+
if (compiled_backend_count() == 0) {
226+
print(stderr,
227+
"imiv: no renderer backend is compiled into this build\n");
228+
} else {
229+
print(stderr,
230+
"imiv: no compiled renderer backend is currently available\n");
231+
for (const BackendRuntimeInfo& info : runtime_backend_info()) {
232+
if (!info.build_info.compiled || info.available)
233+
continue;
234+
print(stderr, "imiv: {} unavailable: {}\n",
235+
info.build_info.display_name,
236+
info.unavailable_reason.empty()
237+
? "backend is unavailable"
238+
: info.unavailable_reason);
239+
}
240+
}
241+
platform_glfw_terminate();
242+
return EXIT_FAILURE;
243+
}
244+
if (requested_backend != BackendKind::Auto
245+
&& requested_backend != active_backend) {
246+
if (!backend_kind_is_compiled(requested_backend)) {
247+
print("imiv: requested backend '{}' is not compiled into this "
248+
"build; using '{}'\n",
249+
backend_cli_name(requested_backend),
250+
backend_runtime_name(active_backend));
251+
} else {
252+
const std::string_view unavailable_reason
253+
= backend_unavailable_reason(requested_backend);
254+
print("imiv: requested backend '{}' is unavailable at runtime{}; "
255+
"using '{}'\n",
256+
backend_cli_name(requested_backend),
257+
unavailable_reason.empty()
258+
? ""
259+
: Strutil::fmt::format(" ({})", unavailable_reason),
260+
backend_runtime_name(active_backend));
261+
}
262+
}
263+
233264
const std::string window_title = build_main_window_title(active_backend);
234265
GLFWwindow* window = platform_glfw_create_main_window(
235266
active_backend, 1600, 900, window_title.c_str(), startup_error);
@@ -371,13 +402,18 @@ run(const AppConfig& config)
371402
if (run_config.verbose) {
372403
print("imiv: bootstrap initialized (backend: {})\n",
373404
backend_runtime_name(active_backend));
374-
for (const BackendInfo& info : compiled_backend_info()) {
375-
print("imiv: backend {} ({}) compiled={} build_default={} "
376-
"platform_default={}\n",
377-
info.display_name, info.cli_name,
378-
info.compiled ? "yes" : "no",
379-
info.active_build ? "yes" : "no",
380-
info.platform_default ? "yes" : "no");
405+
for (const BackendRuntimeInfo& info : runtime_backend_info()) {
406+
print("imiv: backend {} ({}) compiled={} available={} "
407+
"build_default={} platform_default={}{}\n",
408+
info.build_info.display_name, info.build_info.cli_name,
409+
info.build_info.compiled ? "yes" : "no",
410+
info.available ? "yes" : "no",
411+
info.build_info.active_build ? "yes" : "no",
412+
info.build_info.platform_default ? "yes" : "no",
413+
(!info.available && !info.unavailable_reason.empty())
414+
? Strutil::fmt::format(" reason='{}'",
415+
info.unavailable_reason)
416+
: std::string());
381417
}
382418
print("imiv: startup queue has {} image path(s)\n",
383419
run_config.input_paths.size());

src/imiv/imiv_aux_windows.cpp

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -279,24 +279,31 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
279279
ImVec2 backend_row_min(0.0f, 0.0f);
280280
ImVec2 backend_row_max(0.0f, 0.0f);
281281
int backend_button_index = 0;
282-
for (const BackendInfo& info : compiled_backend_info()) {
283-
const bool selected = requested_backend == info.kind;
282+
for (const BackendRuntimeInfo& info : runtime_backend_info()) {
283+
const bool selected = requested_backend == info.build_info.kind;
284284
if (backend_button_index > 0)
285285
ImGui::SameLine(0.0f, spacing);
286-
ImGui::PushID(static_cast<int>(info.kind));
287-
if (!info.compiled)
286+
ImGui::PushID(static_cast<int>(info.build_info.kind));
287+
const bool enabled = info.build_info.compiled && info.available;
288+
if (!enabled)
288289
ImGui::BeginDisabled();
289290
push_preview_active_button_style(selected);
290-
if (ImGui::Button(backend_display_name(info.kind),
291+
if (ImGui::Button(backend_display_name(info.build_info.kind),
291292
ImVec2(button_width, 0.0f))
292-
&& info.compiled) {
293-
ui.renderer_backend = static_cast<int>(info.kind);
293+
&& enabled) {
294+
ui.renderer_backend = static_cast<int>(info.build_info.kind);
295+
}
296+
{
297+
const std::string test_label = std::string("pref-backend:")
298+
+ backend_cli_name(
299+
info.build_info.kind);
300+
register_test_engine_item_label(test_label.c_str());
294301
}
295302
pop_preview_active_button_style(selected);
296-
if (!info.compiled)
303+
if (!enabled)
297304
ImGui::EndDisabled();
298305
register_layout_dump_synthetic_item("button",
299-
backend_display_name(info.kind));
306+
backend_display_name(info.build_info.kind));
300307
const ImVec2 item_min = ImGui::GetItemRectMin();
301308
const ImVec2 item_max = ImGui::GetItemRectMax();
302309
if (!have_backend_row_rect) {
@@ -324,8 +331,14 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
324331
const bool requested_backend_compiled
325332
= requested_backend == BackendKind::Auto
326333
|| backend_kind_is_compiled(requested_backend);
334+
const bool requested_backend_available
335+
= requested_backend == BackendKind::Auto
336+
|| backend_kind_is_available(requested_backend);
327337
const bool invalid_requested_backend = requested_backend != BackendKind::Auto
328338
&& !requested_backend_compiled;
339+
const bool unavailable_requested_backend
340+
= requested_backend != BackendKind::Auto
341+
&& requested_backend_compiled && !requested_backend_available;
329342
if (requested_backend == BackendKind::Auto)
330343
ImGui::TextUnformatted("Stored preference: Auto");
331344
else
@@ -336,6 +349,7 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
336349
ImGui::BeginDisabled();
337350
if (ImGui::SmallButton("Reset to Auto"))
338351
ui.renderer_backend = static_cast<int>(BackendKind::Auto);
352+
register_test_engine_item_label("pref-backend:auto");
339353
register_layout_dump_synthetic_item("button", "Reset to Auto");
340354
if (requested_backend == BackendKind::Auto)
341355
ImGui::EndDisabled();
@@ -348,12 +362,35 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
348362
if (invalid_requested_backend) {
349363
ImGui::TextUnformatted(
350364
"Requested backend is not built in this binary and will be ignored when Preferences closes.");
365+
} else if (unavailable_requested_backend) {
366+
const std::string_view unavailable_reason = backend_unavailable_reason(
367+
requested_backend);
368+
if (unavailable_reason.empty()) {
369+
ImGui::TextUnformatted(
370+
"Requested backend is unavailable at runtime and will be ignored when Preferences closes.");
371+
} else {
372+
ImGui::TextWrapped(
373+
"Requested backend is unavailable at runtime (%s) and will be ignored when Preferences closes.",
374+
std::string(unavailable_reason).c_str());
375+
}
351376
} else if (next_launch_backend != active_backend) {
352377
ImGui::TextUnformatted("Backend change requires restart.");
353378
}
354379
if (requested_backend == BackendKind::Auto) {
355380
ImGui::TextUnformatted("Auto selects the first available backend.");
356381
}
382+
for (const BackendRuntimeInfo& info : runtime_backend_info()) {
383+
if (!info.build_info.compiled || info.available)
384+
continue;
385+
if (info.unavailable_reason.empty()) {
386+
ImGui::Text("%s unavailable",
387+
info.build_info.display_name);
388+
} else {
389+
ImGui::TextWrapped("%s unavailable: %s",
390+
info.build_info.display_name,
391+
info.unavailable_reason.c_str());
392+
}
393+
}
357394

358395
ImGui::Spacing();
359396
ImGui::TextUnformatted("Image Cache max memory (requires restart)");
@@ -449,7 +486,8 @@ draw_preferences_window(PlaceholderUiState& ui, bool& show_window,
449486
ImGui::PopStyleVar();
450487
const BackendKind close_backend = sanitize_backend_kind(ui.renderer_backend);
451488
if (!show_window && close_backend != BackendKind::Auto
452-
&& !backend_kind_is_compiled(close_backend)) {
489+
&& (!backend_kind_is_compiled(close_backend)
490+
|| !backend_kind_is_available(close_backend))) {
453491
ui.renderer_backend = static_cast<int>(BackendKind::Auto);
454492
}
455493
}

0 commit comments

Comments
 (0)