Skip to content

Commit d18525c

Browse files
committed
Vulkan texture retirement, UI and probe fixes
Introduce deferred Vulkan texture destruction and submit-serial tracking to avoid destroying resources still in use: add RetiredVulkanTexture, retire_texture/drain_retired_textures, per-frame submit serial bookkeeping, and flush retired textures on device idle/cleanup. Replace immediate destroy with destroy_now for safe teardown. Fix renderer wait-idle error reporting on shutdown. Misc UI and UX fixes: sync per-view fit_image_to_window state, only zoom with mouse wheel when the image canvas accepts mouse input, improve pixel-closeup numeric formatting and layout (new format_probe_display_value, stable column layout, fixed mono font size). Add regression test steps to verify zoom-after-fit interactions. Also add a new design doc (imiv_tiling.md) describing a tiled large-image architecture and phased implementation guidance. Signed-off-by: Vlad (Kuzmin) Erium <libalias@gmail.com> Signed-off-by: Vlad <shaamaan@gmail.com>
1 parent 604759f commit d18525c

11 files changed

+736
-182
lines changed

src/imiv/imiv_app.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,10 @@ run(const AppConfig& config)
769769
continue;
770770
renderer_destroy_texture(renderer_state, view->viewer.texture);
771771
}
772+
if (!renderer_wait_idle(renderer_state, prefs_save_error)
773+
&& !prefs_save_error.empty())
774+
print(stderr, "imiv: failed to finalize renderer idle: {}\n",
775+
prefs_save_error);
772776
#if defined(IMGUI_ENABLE_TEST_ENGINE)
773777
test_engine_stop(test_engine_runtime);
774778
#endif

src/imiv/imiv_frame.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,7 @@ draw_viewer_ui(MultiViewWorkspace& workspace, ImageLibraryState& library,
15341534
active && actions.recenter_requested);
15351535
ImGui::End();
15361536
if (active) {
1537+
ui_state.fit_image_to_window = view_ui_state.fit_image_to_window;
15371538
ui_state.image_window_force_dock = view_ui_state.image_window_force_dock;
15381539
}
15391540
}

src/imiv/imiv_image_view.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ draw_image_window_contents(ViewerState& viewer, PlaceholderUiState& ui_state,
470470
viewer.zoom_drag_active = false;
471471
}
472472

473-
if (viewport_accepts_mouse && io.MouseWheel != 0.0f) {
473+
if (image_canvas_accepts_mouse && io.MouseWheel != 0.0f) {
474474
const float scale = (io.MouseWheel > 0.0f) ? 1.1f : 0.9f;
475475
request_zoom_scale(pending_zoom, scale, true);
476476
}

src/imiv/imiv_overlays.cpp

Lines changed: 36 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -227,51 +227,26 @@ namespace {
227227
}
228228
}
229229

230-
std::string format_probe_iv_float(double value)
230+
std::string format_probe_display_value(double value)
231231
{
232-
if (value < 10.0)
233-
return Strutil::fmt::format("{:.3f}", value);
234-
if (value < 100.0)
235-
return Strutil::fmt::format("{:.2f}", value);
236-
if (value < 1000.0)
237-
return Strutil::fmt::format("{:.1f}", value);
238-
return Strutil::fmt::format("{:.0f}", value);
239-
}
232+
if (!std::isfinite(value))
233+
return Strutil::fmt::format("{:>8}", value);
240234

241-
std::string format_probe_integer_trunc(UploadDataType type, double value)
242-
{
243-
switch (type) {
244-
case UploadDataType::UInt8:
245-
return Strutil::fmt::format("{}",
246-
static_cast<unsigned int>(
247-
std::clamp(value, 0.0, 255.0)));
248-
case UploadDataType::UInt16:
249-
return Strutil::fmt::format("{}",
250-
static_cast<unsigned int>(
251-
std::clamp(value, 0.0, 65535.0)));
252-
case UploadDataType::UInt32:
253-
return Strutil::fmt::format("{}", static_cast<uint32_t>(
254-
std::clamp(value, 0.0,
255-
4294967295.0)));
256-
default: break;
257-
}
258-
return Strutil::fmt::format("{:.0f}", value);
259-
}
260-
261-
bool probe_type_is_integer(UploadDataType type)
262-
{
263-
return type == UploadDataType::UInt8 || type == UploadDataType::UInt16;
264-
}
235+
const double abs_value = std::abs(value);
236+
if ((abs_value > 99999.0) || (abs_value > 0.0 && abs_value < 0.00001))
237+
return Strutil::fmt::format("{: .1e}", value);
265238

266-
double probe_channel_integer_denominator(UploadDataType type)
267-
{
268-
switch (type) {
269-
case UploadDataType::UInt8: return 255.0;
270-
case UploadDataType::UInt16: return 65535.0;
271-
case UploadDataType::UInt32: return 4294967295.0;
272-
default: break;
239+
int digits_before_decimal = 1;
240+
if (abs_value >= 1.0) {
241+
digits_before_decimal
242+
= static_cast<int>(std::floor(std::log10(abs_value))) + 1;
273243
}
274-
return 1.0;
244+
digits_before_decimal = std::clamp(digits_before_decimal, 1, 5);
245+
const int decimals = std::clamp(6 - digits_before_decimal, 1, 5);
246+
247+
const std::string format
248+
= Strutil::fmt::format("{{: .{}f}}", decimals);
249+
return Strutil::fmt::format(format, value);
275250
}
276251

277252
OverlayPanelRect
@@ -563,16 +538,14 @@ draw_pixel_closeup_overlay(const ViewerState& viewer,
563538

564539
std::vector<std::string> lines;
565540
std::vector<ImU32> line_colors;
566-
lines.emplace_back("Pixel Closeup:");
567-
line_colors.emplace_back(IM_COL32(240, 242, 245, 255));
568541
if (viewer.image.path.empty()) {
569542
lines.emplace_back("No image loaded.");
570543
line_colors.emplace_back(IM_COL32(240, 242, 245, 255));
571544
} else if (!viewer.probe_valid) {
572545
lines.emplace_back("Hover over image to inspect.");
573546
line_colors.emplace_back(IM_COL32(240, 242, 245, 255));
574547
} else {
575-
lines.emplace_back(Strutil::fmt::format(" ({:d},{:d})",
548+
lines.emplace_back(Strutil::fmt::format("X: {:>7} Y: {:>7}",
576549
viewer.probe_x,
577550
viewer.probe_y));
578551
line_colors.emplace_back(IM_COL32(0, 255, 255, 220));
@@ -581,67 +554,37 @@ draw_pixel_closeup_overlay(const ViewerState& viewer,
581554
std::vector<double> max_values;
582555
std::vector<double> avg_values;
583556
int sample_count = 0;
584-
const bool integer_type = probe_type_is_integer(viewer.image.type);
585-
const ProbeStatsSemantics preview_semantics
586-
= integer_type ? ProbeStatsSemantics::RawStored
587-
: ProbeStatsSemantics::OIIOFloat;
588557
const bool have_stats
589558
= compute_area_stats(viewer.image, viewer.probe_x, viewer.probe_y,
590559
ui_state.closeup_avg_pixels, min_values,
591560
max_values, avg_values, sample_count,
592-
preview_semantics);
561+
ProbeStatsSemantics::OIIOFloat);
593562

594-
const double denom = probe_channel_integer_denominator(
595-
viewer.image.type);
596-
if (integer_type) {
597-
lines.emplace_back(" Val Norm Min Max Avg");
598-
} else {
599-
lines.emplace_back(" Val Min Max Avg");
600-
}
563+
lines.emplace_back("");
564+
line_colors.emplace_back(IM_COL32(240, 242, 245, 255));
565+
lines.emplace_back(Strutil::fmt::format(
566+
"{:<2} {:>8} {:>8} {:>8} {:>8}", "", "Val", "Min", "Max",
567+
"Avg"));
601568
line_colors.emplace_back(IM_COL32(255, 255, 160, 220));
602569
for (size_t c = 0; c < viewer.probe_channels.size(); ++c) {
603570
const std::string label
604571
= pixel_preview_channel_label(viewer.image,
605572
static_cast<int>(c));
606573
const double semantic_value
607-
= integer_type
608-
? viewer.probe_channels[c]
609-
: probe_value_to_oiio_float(viewer.image.type,
610-
viewer.probe_channels[c]);
611-
const std::string value
612-
= integer_type
613-
? format_probe_integer_trunc(viewer.image.type,
614-
viewer.probe_channels[c])
615-
: format_probe_iv_float(semantic_value);
574+
= probe_value_to_oiio_float(viewer.image.type,
575+
viewer.probe_channels[c]);
576+
const std::string value = format_probe_display_value(semantic_value);
616577
if (have_stats && c < min_values.size() && c < max_values.size()
617578
&& c < avg_values.size()) {
618-
if (integer_type && denom > 0.0) {
619-
lines.emplace_back(Strutil::fmt::format(
620-
"{:<2}: {:>5} {:>6.3f} {:>3} {:>3} {:>3}", label,
621-
value, viewer.probe_channels[c] / denom,
622-
format_probe_integer_trunc(viewer.image.type,
623-
min_values[c]),
624-
format_probe_integer_trunc(viewer.image.type,
625-
max_values[c]),
626-
format_probe_integer_trunc(viewer.image.type,
627-
avg_values[c])));
628-
} else {
629-
lines.emplace_back(Strutil::fmt::format(
630-
"{:<2}: {:>6} {:>6} {:>6} {:>6}", label, value,
631-
format_probe_iv_float(min_values[c]),
632-
format_probe_iv_float(max_values[c]),
633-
format_probe_iv_float(avg_values[c])));
634-
}
579+
lines.emplace_back(Strutil::fmt::format(
580+
"{:<2} {:>8} {:>8} {:>8} {:>8}", label, value,
581+
format_probe_display_value(min_values[c]),
582+
format_probe_display_value(max_values[c]),
583+
format_probe_display_value(avg_values[c])));
635584
} else {
636-
if (integer_type) {
637-
lines.emplace_back(Strutil::fmt::format(
638-
"{:<2}: {:>5} {:>6} {:>3} {:>3} {:>3}", label,
639-
value, "-----", "---", "---", "---"));
640-
} else {
641-
lines.emplace_back(Strutil::fmt::format(
642-
"{:<2}: {:>6} {:>6} {:>6} {:>6}", label, value,
643-
"-----", "-----", "-----"));
644-
}
585+
lines.emplace_back(Strutil::fmt::format(
586+
"{:<2} {:>8} {:>8} {:>8} {:>8}", label, value, "-----",
587+
"-----", "-----"));
645588
}
646589
ImU32 channel_color = IM_COL32(220, 220, 220, 255);
647590
if (!label.empty()) {
@@ -666,9 +609,8 @@ draw_pixel_closeup_overlay(const ViewerState& viewer,
666609
const float text_to_window_gap = 2.0f;
667610
const float text_wrap_w = std::max(8.0f,
668611
closeup_window_size - text_pad_x * 2.0f);
669-
ImFont* text_font = fonts.mono ? fonts.mono : ImGui::GetFont();
670-
const float text_font_size = text_font ? text_font->LegacySize
671-
: ImGui::GetFontSize();
612+
ImFont* text_font = fonts.mono ? fonts.mono : ImGui::GetFont();
613+
const float text_font_size = 13.5f;
672614

673615
float text_panel_h = text_pad_y * 2.0f;
674616
for (const std::string& line : lines) {

src/imiv/imiv_renderer_vulkan.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ vulkan_destroy_texture(RendererState& renderer_state, RendererTexture& texture)
139139
VulkanTexture* vk_texture = reinterpret_cast<VulkanTexture*>(
140140
texture.backend);
141141
if (vk_state != nullptr && vk_texture != nullptr)
142-
destroy_texture(*vk_state, *vk_texture);
142+
retire_texture(*vk_state, *vk_texture);
143143
delete vk_texture;
144144
}
145145

@@ -277,6 +277,7 @@ vulkan_wait_idle(RendererState& renderer_state, std::string& error_message)
277277
}
278278
const VkResult err = vkDeviceWaitIdle(vk_state->device);
279279
if (err == VK_SUCCESS) {
280+
drain_retired_textures(*vk_state, true);
280281
error_message.clear();
281282
return true;
282283
}

0 commit comments

Comments
 (0)