Plan renderer diagnostics in app status

This commit is contained in:
2026-06-03 19:49:54 +02:00
parent fa1493b843
commit 164f99fe48
8 changed files with 151 additions and 17 deletions

View File

@@ -17,6 +17,7 @@ and validation command.
| Version metadata | `scripts/pre-build.py`, `version.*` | build system, `pp_foundation` | Generated header smoke test, missing-tag behavior |
| Thumbnail generation/read | `Canvas`, `Image` | `pp_assets`, `pp_paint_renderer` | Golden thumbnail, corrupt input, destination-feedback copy/fetch gate |
| Save-as, overwrite prompts | App/dialogs | `pp_app_core`, `pp_panopainter_ui`, `pp_platform_*` | Decision tests, UI automation, and platform smoke |
| App status and renderer diagnostics | App title/status widgets, extension indicators | `pp_app_core`, `pp_renderer_api`, `pp_panopainter_ui` | Title/status text tests, renderer diagnostic indicator tests, CLI status smoke, live layout adapter smoke |
## Image And Export

File diff suppressed because one or more lines are too long

View File

@@ -182,9 +182,11 @@ the live tools/options menu and `pano_cli plan-app-preferences` consume those
contracts while legacy widgets and settings persistence execute them.
It also owns tested app status/display plans for document title text,
resolution mapping/labels, DPI text, history-memory text, and recording-frame
status text; `App::title_update`, `App::update_memory_usage`,
`App::update_rec_frames`, resolution helpers, and `pano_cli plan-app-status`
consume those contracts while legacy UI nodes still render the strings.
status text, plus renderer diagnostic indicator labels for framebuffer fetch
and floating-point render targets; `App::title_update`,
`App::update_memory_usage`, `App::update_rec_frames`, resolution helpers,
`App::initLayout`, and `pano_cli plan-app-status` consume those contracts while
legacy UI nodes still render the strings and status lights.
`panopainter_app` is now a real static target that owns app orchestration
sources, app version metadata, and version-header generation.
`pp_panopainter_ui` now owns app-specific modal, dialog, panel, canvas,

View File

@@ -33,6 +33,23 @@ struct RecordingFrameLabel {
std::string text;
};
struct RendererDiagnosticsInput {
bool framebuffer_fetch = false;
bool float32_render_targets = false;
bool float32_linear_filtering = false;
bool float16_render_targets = false;
};
struct RendererDiagnosticIndicator {
bool supported = false;
std::string_view label;
};
struct RendererDiagnosticsPlan {
RendererDiagnosticIndicator framebuffer_fetch;
RendererDiagnosticIndicator floating_point_targets;
};
[[nodiscard]] inline pp::foundation::Result<int> display_resolution_from_index(int index)
{
if (index < 0 || static_cast<std::size_t>(index) >= document_resolution_values.size()) {
@@ -116,4 +133,38 @@ struct RecordingFrameLabel {
};
}
[[nodiscard]] inline RendererDiagnosticsPlan plan_renderer_diagnostics(
RendererDiagnosticsInput input) noexcept
{
RendererDiagnosticsPlan plan;
plan.framebuffer_fetch = {
input.framebuffer_fetch,
"FBF",
};
if (input.float32_linear_filtering) {
plan.floating_point_targets = {
true,
"F32L",
};
} else if (input.float32_render_targets) {
plan.floating_point_targets = {
true,
"F32",
};
} else if (input.float16_render_targets) {
plan.floating_point_targets = {
true,
"F16",
};
} else {
plan.floating_point_targets = {
false,
"",
};
}
return plan;
}
}

View File

@@ -2179,23 +2179,28 @@ void App::initLayout()
version_label->set_text(g_version);
}
const auto renderer_features = ShaderManager::render_device_features();
const auto renderer_diagnostics = pp::app::plan_renderer_diagnostics({
.framebuffer_fetch = renderer_features.framebuffer_fetch,
.float32_render_targets = renderer_features.float32_render_targets,
.float32_linear_filtering = ShaderManager::ext_float32_linear,
.float16_render_targets = renderer_features.float16_render_targets,
});
if (auto x = layout[main_id]->find<NodeBorder>("ext-fbf"))
{
x->m_color = ShaderManager::ext_framebuffer_fetch ? glm::vec4(0, 1, 0, 1) : glm::vec4(1, 0, 0, 1);
x->m_color = renderer_diagnostics.framebuffer_fetch.supported ?
glm::vec4(0, 1, 0, 1) :
glm::vec4(1, 0, 0, 1);
}
if (auto x = layout[main_id]->find<NodeBorder>("ext-flt"))
{
if (ShaderManager::ext_float32 || ShaderManager::ext_float16)
if (renderer_diagnostics.floating_point_targets.supported)
{
if (auto t = x->find<NodeText>("ext-flt-text"))
{
if (ShaderManager::ext_float32_linear)
t->set_text("F32L");
else if (ShaderManager::ext_float32)
t->set_text("F32");
else if (ShaderManager::ext_float16)
t->set_text("F16");
t->set_text(std::string(renderer_diagnostics.floating_point_targets.label));
}
x->m_color = glm::vec4(0, 1, 0, 1);
}

View File

@@ -905,16 +905,20 @@ if(TARGET pano_cli)
--history-bytes 1572864
--recording-running
--encoder-available
--encoded-frames 12)
--encoded-frames 12
--framebuffer-fetch
--float32
--float32-linear
--float16)
set_tests_properties(pano_cli_plan_app_status_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-status\".*\"title\":\"Panodoc: demo\\* \\(8K\\)\".*\"dpi\":\"1.3x-dpi\".*\"memory\":\"History memory: 1.50 Mb\".*\"recording\":\\{\"visible\":true,\"text\":\"Recorded 12 frames\"\\}.*\"fromIndexValid\":true.*\"fromIndex\":2048.*\"toIndexValid\":true.*\"toIndex\":3.*\"labelValid\":true.*\"label\":\"8K\"")
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-status\".*\"title\":\"Panodoc: demo\\* \\(8K\\)\".*\"dpi\":\"1.3x-dpi\".*\"memory\":\"History memory: 1.50 Mb\".*\"recording\":\\{\"visible\":true,\"text\":\"Recorded 12 frames\"\\}.*\"rendererDiagnostics\":\\{\"framebufferFetch\":\\{\"supported\":true,\"label\":\"FBF\"\\},\"floatingPointTargets\":\\{\"supported\":true,\"label\":\"F32L\"\\}\\}.*\"fromIndexValid\":true.*\"fromIndex\":2048.*\"toIndexValid\":true.*\"toIndex\":3.*\"labelValid\":true.*\"label\":\"8K\"")
add_test(NAME pano_cli_plan_app_status_unknown_resolution_smoke
COMMAND pano_cli plan-app-status --doc-name demo --resolution 1234 --resolution-index 9 --recording-running)
set_tests_properties(pano_cli_plan_app_status_unknown_resolution_smoke PROPERTIES
LABELS "app;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-status\".*\"title\":\"Panodoc: demo \\(unknown\\)\".*\"recording\":\\{\"visible\":false,\"text\":\"\"\\}.*\"fromIndexValid\":false.*\"toIndexValid\":false.*\"labelValid\":false.*\"label\":\"\"")
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-status\".*\"title\":\"Panodoc: demo \\(unknown\\)\".*\"recording\":\\{\"visible\":false,\"text\":\"\"\\}.*\"rendererDiagnostics\":\\{\"framebufferFetch\":\\{\"supported\":false,\"label\":\"FBF\"\\},\"floatingPointTargets\":\\{\"supported\":false,\"label\":\"\"\\}\\}.*\"fromIndexValid\":false.*\"toIndexValid\":false.*\"labelValid\":false.*\"label\":\"\"")
add_test(NAME pano_cli_plan_document_resize_smoke
COMMAND pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4)

View File

@@ -71,6 +71,47 @@ void recording_label_only_shows_when_recording_with_encoder(pp::tests::Harness&
PP_EXPECT(harness, active.text == "Recorded 12 frames");
}
void renderer_diagnostics_report_framebuffer_fetch(pp::tests::Harness& harness)
{
const auto unsupported = pp::app::plan_renderer_diagnostics({});
PP_EXPECT(harness, !unsupported.framebuffer_fetch.supported);
PP_EXPECT(harness, unsupported.framebuffer_fetch.label == "FBF");
const auto supported = pp::app::plan_renderer_diagnostics({
.framebuffer_fetch = true,
});
PP_EXPECT(harness, supported.framebuffer_fetch.supported);
PP_EXPECT(harness, supported.framebuffer_fetch.label == "FBF");
}
void renderer_diagnostics_prefer_highest_float_capability_label(pp::tests::Harness& harness)
{
const auto none = pp::app::plan_renderer_diagnostics({});
PP_EXPECT(harness, !none.floating_point_targets.supported);
PP_EXPECT(harness, none.floating_point_targets.label.empty());
const auto float16 = pp::app::plan_renderer_diagnostics({
.float16_render_targets = true,
});
PP_EXPECT(harness, float16.floating_point_targets.supported);
PP_EXPECT(harness, float16.floating_point_targets.label == "F16");
const auto float32 = pp::app::plan_renderer_diagnostics({
.float32_render_targets = true,
.float16_render_targets = true,
});
PP_EXPECT(harness, float32.floating_point_targets.supported);
PP_EXPECT(harness, float32.floating_point_targets.label == "F32");
const auto float32_linear = pp::app::plan_renderer_diagnostics({
.float32_render_targets = true,
.float32_linear_filtering = true,
.float16_render_targets = true,
});
PP_EXPECT(harness, float32_linear.floating_point_targets.supported);
PP_EXPECT(harness, float32_linear.floating_point_targets.label == "F32L");
}
}
int main()
@@ -82,5 +123,7 @@ int main()
harness.run("document title survives unknown resolution", document_title_survives_unknown_resolution);
harness.run("status labels match legacy text", status_labels_match_legacy_text);
harness.run("recording label only shows when recording with encoder", recording_label_only_shows_when_recording_with_encoder);
harness.run("renderer diagnostics report framebuffer fetch", renderer_diagnostics_report_framebuffer_fetch);
harness.run("renderer diagnostics prefer highest float capability label", renderer_diagnostics_prefer_highest_float_capability_label);
return harness.finish();
}

View File

@@ -236,6 +236,10 @@ struct PlanAppStatusArgs {
bool recording_running = false;
bool encoder_available = false;
std::uint32_t encoded_frames = 0;
bool framebuffer_fetch = false;
bool float32_render_targets = false;
bool float32_linear_filtering = false;
bool float16_render_targets = false;
};
struct PlanDocumentResizeArgs {
@@ -1766,7 +1770,7 @@ void print_help()
<< " plan-cloud-upload-all [--file-count N] [--no-progress-ui]\n"
<< " plan-recording-session [--running] [--frame-count N] [--platform-deletes-recorded-files]\n"
<< " plan-app-preferences [--ui-scale N] [--display-density N] [--current-scale N] [--scale-option N] [--viewport-scale N] [--rtl] [--timelapse-disabled] [--recording-running] [--vr-controllers-disabled] [--cursor-mode N]\n"
<< " plan-app-status [--doc-name NAME] [--unsaved] [--resolution N] [--resolution-index N] [--zoom N] [--history-bytes N] [--recording-running] [--encoder-available] [--encoded-frames N]\n"
<< " plan-app-status [--doc-name NAME] [--unsaved] [--resolution N] [--resolution-index N] [--zoom N] [--history-bytes N] [--recording-running] [--encoder-available] [--encoded-frames N] [--framebuffer-fetch] [--float32] [--float32-linear] [--float16]\n"
<< " plan-tools-menu --command panels|options|clear-grids|reset-camera|shortcuts|sonarpen [--sonarpen-available]\n"
<< " plan-tools-panel --panel presets|color|color-advanced|layers|brush|grids|animation [--already-visible]\n"
<< " plan-about-menu --command help|about|news|crash|performance [--version-major N] [--version-minor N] [--version-fix N] [--no-diagnostics] [--no-canvas]\n"
@@ -3558,6 +3562,14 @@ pp::foundation::Status parse_plan_app_status_args(
args.recording_running = true;
} else if (key == "--encoder-available") {
args.encoder_available = true;
} else if (key == "--framebuffer-fetch") {
args.framebuffer_fetch = true;
} else if (key == "--float32") {
args.float32_render_targets = true;
} else if (key == "--float32-linear") {
args.float32_linear_filtering = true;
} else if (key == "--float16") {
args.float16_render_targets = true;
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
@@ -3582,6 +3594,12 @@ int plan_app_status(int argc, char** argv)
args.recording_running,
args.encoder_available,
static_cast<int>(args.encoded_frames));
const auto diagnostics = pp::app::plan_renderer_diagnostics({
.framebuffer_fetch = args.framebuffer_fetch,
.float32_render_targets = args.float32_render_targets,
.float32_linear_filtering = args.float32_linear_filtering,
.float16_render_targets = args.float16_render_targets,
});
std::cout << "{\"ok\":true,\"command\":\"plan-app-status\""
<< ",\"state\":{\"documentName\":\"" << json_escape(args.document_name)
@@ -3593,6 +3611,10 @@ int plan_app_status(int argc, char** argv)
<< ",\"recordingRunning\":" << json_bool(args.recording_running)
<< ",\"encoderAvailable\":" << json_bool(args.encoder_available)
<< ",\"encodedFrames\":" << args.encoded_frames
<< ",\"framebufferFetch\":" << json_bool(args.framebuffer_fetch)
<< ",\"float32\":" << json_bool(args.float32_render_targets)
<< ",\"float32Linear\":" << json_bool(args.float32_linear_filtering)
<< ",\"float16\":" << json_bool(args.float16_render_targets)
<< "},\"title\":\"" << json_escape(pp::app::make_document_title(
args.document_name,
args.unsaved,
@@ -3601,7 +3623,13 @@ int plan_app_status(int argc, char** argv)
<< "\",\"memory\":\"" << json_escape(pp::app::make_history_memory_label(args.history_bytes))
<< "\",\"recording\":{\"visible\":" << json_bool(recording_label.visible)
<< ",\"text\":\"" << json_escape(recording_label.text)
<< "\"},\"resolutionMap\":{\"fromIndexValid\":" << json_bool(static_cast<bool>(resolution_from_index))
<< "\"},\"rendererDiagnostics\":{\"framebufferFetch\":{\"supported\":"
<< json_bool(diagnostics.framebuffer_fetch.supported)
<< ",\"label\":\"" << json_escape(std::string(diagnostics.framebuffer_fetch.label))
<< "\"},\"floatingPointTargets\":{\"supported\":"
<< json_bool(diagnostics.floating_point_targets.supported)
<< ",\"label\":\"" << json_escape(std::string(diagnostics.floating_point_targets.label))
<< "\"}},\"resolutionMap\":{\"fromIndexValid\":" << json_bool(static_cast<bool>(resolution_from_index))
<< ",\"fromIndex\":" << (resolution_from_index ? resolution_from_index.value() : 0)
<< ",\"toIndexValid\":" << json_bool(static_cast<bool>(resolution_index))
<< ",\"toIndex\":" << (resolution_index ? resolution_index.value() : 0)