Extend app frame planning to tick and resize
This commit is contained in:
@@ -242,10 +242,11 @@ Known local toolchain state:
|
||||
UI render-target creation, recording startup, VR-controller state mutation,
|
||||
settings writes, and license-warning dialogs remain legacy execution.
|
||||
- `src/app_core/app_frame.h` owns the current initial surface, update-gating,
|
||||
and draw-pass decisions consumed by `App::create`, `App::update`,
|
||||
`App::draw`, and `pano_cli plan-app-frame`; retained layout traversal,
|
||||
toolbar widget writes, canvas stroke drawing, VR UI render-target drawing,
|
||||
main target binding, and OpenGL/UI drawing remain in the legacy app.
|
||||
tick, resize, and draw-pass decisions consumed by `App::create`,
|
||||
`App::tick`, `App::resize`, `App::update`, `App::draw`, and
|
||||
`pano_cli plan-app-frame`; retained layout traversal, toolbar widget writes,
|
||||
UI render-target recreation, canvas stroke drawing, VR UI render-target
|
||||
drawing, main target binding, and OpenGL/UI drawing remain in the legacy app.
|
||||
- `src/app_core/app_shutdown.h` owns the current shutdown cleanup staging
|
||||
consumed by `App::terminate` and `pano_cli plan-app-shutdown`; retained
|
||||
UI-state save, recording stop, resource invalidation, layout unload,
|
||||
@@ -836,7 +837,9 @@ Known local toolchain state:
|
||||
split persistence/runtime dispatch, and malformed startup-plan rejection.
|
||||
- `pp_app_core_app_frame_tests` covers the legacy initial surface default,
|
||||
idle/redraw/animation update gating, canvas-stroke draw eligibility, VR UI
|
||||
visibility, main UI suppression in VR-only mode, and redraw reset planning.
|
||||
visibility, main UI suppression in VR-only mode, tick layout selection,
|
||||
resize render-target/redraw projection, invalid resize rejection, and redraw
|
||||
reset planning.
|
||||
- `pp_app_core_app_shutdown_tests` covers legacy shutdown cleanup staging for
|
||||
UI-state save, stroke-preview renderer shutdown, recording stop,
|
||||
texture/shader invalidation, layout unload, render-target/mesh destruction,
|
||||
|
||||
@@ -92,11 +92,12 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
and runtime side effects through the startup bridge. `pano_cli
|
||||
plan-app-startup-resources` exposes the resource path for automation.
|
||||
- 2026-06-05: DEBT-0003 was narrowed. Initial surface sizing, redraw/animation
|
||||
update gating, canvas-stroke draw eligibility, VR UI pass selection, main UI
|
||||
pass selection, and redraw reset are now tested `pp_app_core` frame plans
|
||||
consumed by `App::create`, `App::update`, `App::draw`, and
|
||||
update gating, layout tick selection, resize render-target recreation,
|
||||
canvas-stroke draw eligibility, VR UI pass selection, main UI pass selection,
|
||||
and redraw reset are now tested `pp_app_core` frame plans consumed by
|
||||
`App::create`, `App::tick`, `App::resize`, `App::update`, `App::draw`, and
|
||||
`pano_cli plan-app-frame`; retained layout traversal, toolbar widget writes,
|
||||
and OpenGL/UI drawing remain in the legacy app.
|
||||
render-target recreation, and OpenGL/UI drawing remain in the legacy app.
|
||||
- 2026-06-05: DEBT-0003 was narrowed again. Shutdown cleanup staging for
|
||||
UI-state save, stroke-preview renderer shutdown, recording stop,
|
||||
texture/shader invalidation, layout unload, UI render-target and face-plane
|
||||
|
||||
@@ -199,10 +199,11 @@ and floating-point render targets; `App::title_update`,
|
||||
`App::initLayout`, and `pano_cli plan-app-status` consume those contracts while
|
||||
legacy UI nodes still render the strings and status lights.
|
||||
Frame-level app decisions for the initial surface size, redraw/animation update
|
||||
gating, canvas-stroke drawing, VR UI drawing, main UI drawing, and redraw reset
|
||||
now live in `pp_app_core`; `App::create`, `App::update`, `App::draw`, and
|
||||
`pano_cli plan-app-frame` consume those plans while retained layout traversal
|
||||
and OpenGL/UI drawing stay in the legacy app.
|
||||
gating, layout ticking, resize render-target recreation, canvas-stroke drawing,
|
||||
VR UI drawing, main UI drawing, and redraw reset now live in `pp_app_core`;
|
||||
`App::create`, `App::tick`, `App::resize`, `App::update`, `App::draw`, and
|
||||
`pano_cli plan-app-frame` consume those plans while retained layout traversal,
|
||||
render-target recreation, and OpenGL/UI drawing stay in the legacy app.
|
||||
Shutdown lifecycle staging for UI-state save, stroke-preview renderer shutdown,
|
||||
recording stop, texture/shader invalidation, layout unload, render-target
|
||||
destruction, panel-node release, and quick-mode cleanup now lives in
|
||||
@@ -1654,10 +1655,13 @@ Results:
|
||||
sequencing also covered by `pano_cli_plan_app_startup_resources_smoke` and
|
||||
`pano_cli_plan_app_startup_resources_rejects_bad_size`.
|
||||
- `PanoPainter`, `pp_app_core_app_frame_tests`, and `pano_cli` built after
|
||||
app frame surface/update/draw-pass decisions moved into `pp_app_core`.
|
||||
app frame surface/update/tick/resize/draw-pass decisions moved into
|
||||
`pp_app_core`.
|
||||
- Focused frame CTest coverage passed for `pp_app_core_app_frame_tests`,
|
||||
`pano_cli_plan_app_frame_vr_smoke`, and
|
||||
`pano_cli_plan_app_frame_idle_missing_canvas_smoke`.
|
||||
`pano_cli_plan_app_frame_idle_missing_canvas_smoke`, with resize automation
|
||||
covered by `pano_cli_plan_app_frame_resize_smoke` and
|
||||
`pano_cli_plan_app_frame_rejects_bad_resize`.
|
||||
- `PanoPainter`, `pp_app_core_app_shutdown_tests`, and `pano_cli` built after
|
||||
shutdown cleanup staging moved into `pp_app_core`.
|
||||
- Focused shutdown CTest coverage passed for `pp_app_core_app_shutdown_tests`,
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "foundation/result.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace pp::app {
|
||||
|
||||
struct AppInitialSurfacePlan {
|
||||
@@ -20,6 +25,20 @@ struct AppFrameDrawPlan {
|
||||
bool reset_redraw = true;
|
||||
};
|
||||
|
||||
struct AppFrameTickPlan {
|
||||
bool tick_designer_layout = false;
|
||||
bool tick_main_layout = false;
|
||||
};
|
||||
|
||||
struct AppResizePlan {
|
||||
float width = 0.0F;
|
||||
float height = 0.0F;
|
||||
int render_target_width = 0;
|
||||
int render_target_height = 0;
|
||||
bool recreate_ui_render_target = true;
|
||||
bool request_redraw = true;
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr AppInitialSurfacePlan plan_app_initial_surface() noexcept
|
||||
{
|
||||
return AppInitialSurfacePlan {
|
||||
@@ -53,4 +72,42 @@ struct AppFrameDrawPlan {
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr AppFrameTickPlan plan_app_frame_tick(
|
||||
bool has_designer_layout,
|
||||
bool has_main_layout) noexcept
|
||||
{
|
||||
return AppFrameTickPlan {
|
||||
.tick_designer_layout = has_designer_layout,
|
||||
.tick_main_layout = has_main_layout,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<AppResizePlan> plan_app_resize(float width, float height)
|
||||
{
|
||||
if (!std::isfinite(width) || !std::isfinite(height)) {
|
||||
return pp::foundation::Result<AppResizePlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("resize dimensions must be finite"));
|
||||
}
|
||||
|
||||
if (width < 1.0F || height < 1.0F) {
|
||||
return pp::foundation::Result<AppResizePlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("resize dimensions must be positive"));
|
||||
}
|
||||
|
||||
if (width > static_cast<float>(std::numeric_limits<int>::max())
|
||||
|| height > static_cast<float>(std::numeric_limits<int>::max())) {
|
||||
return pp::foundation::Result<AppResizePlan>::failure(
|
||||
pp::foundation::Status::out_of_range("resize dimensions exceed integer range"));
|
||||
}
|
||||
|
||||
return pp::foundation::Result<AppResizePlan>::success(AppResizePlan {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.render_target_width = static_cast<int>(width),
|
||||
.render_target_height = static_cast<int>(height),
|
||||
.recreate_ui_render_target = true,
|
||||
.request_redraw = true,
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace pp::app
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "app_core/app_frame.h"
|
||||
#include "app_core/document_platform_io.h"
|
||||
#include "app_core/document_sharing.h"
|
||||
#include "platform_api/platform_services.h"
|
||||
@@ -89,19 +90,35 @@ void App::crash_test()
|
||||
|
||||
void App::tick(float dt)
|
||||
{
|
||||
if (auto* main = layout_designer[main_id])
|
||||
const auto tick_plan = pp::app::plan_app_frame_tick(
|
||||
layout_designer.get(main_id) != nullptr,
|
||||
layout.get(main_id) != nullptr);
|
||||
if (auto* main = layout_designer[main_id]; tick_plan.tick_designer_layout && main)
|
||||
main->tick(dt);
|
||||
if (auto* main = layout[main_id])
|
||||
if (auto* main = layout[main_id]; tick_plan.tick_main_layout && main)
|
||||
main->tick(dt);
|
||||
}
|
||||
|
||||
void App::resize(float w, float h)
|
||||
{
|
||||
LOG("App::resize %d %d", (int)w, (int)h);
|
||||
uirtt.create(static_cast<int>(w), static_cast<int>(h), -1, rgba8_internal_format(), true);
|
||||
redraw = true;
|
||||
width = w;
|
||||
height = h;
|
||||
const auto resize_plan = pp::app::plan_app_resize(w, h);
|
||||
if (!resize_plan) {
|
||||
LOG("App resize plan failed: %s", resize_plan.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resize_plan.value().recreate_ui_render_target) {
|
||||
uirtt.create(
|
||||
resize_plan.value().render_target_width,
|
||||
resize_plan.value().render_target_height,
|
||||
-1,
|
||||
rgba8_internal_format(),
|
||||
true);
|
||||
}
|
||||
redraw = resize_plan.value().request_redraw;
|
||||
width = resize_plan.value().width;
|
||||
height = resize_plan.value().height;
|
||||
}
|
||||
|
||||
void App::show_cursor()
|
||||
|
||||
@@ -974,10 +974,23 @@ if(TARGET pano_cli)
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"surface\":\\{\"width\":960,\"height\":540\\}.*\"updateFrame\":true.*\"updateLayouts\":true.*\"refreshCanvasToolbar\":true.*\"drawCanvasStroke\":true.*\"drawVrUi\":true.*\"drawMainUi\":true.*\"resetRedraw\":true")
|
||||
|
||||
add_test(NAME pano_cli_plan_app_frame_idle_missing_canvas_smoke
|
||||
COMMAND pano_cli plan-app-frame --no-canvas --ui-hidden --vr-only)
|
||||
COMMAND pano_cli plan-app-frame --no-canvas --ui-hidden --vr-only --no-designer-layout)
|
||||
set_tests_properties(pano_cli_plan_app_frame_idle_missing_canvas_smoke PROPERTIES
|
||||
LABELS "app;integration;desktop-fast;fuzz"
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"updateFrame\":false.*\"drawCanvasStroke\":false.*\"drawVrUi\":false.*\"drawMainUi\":false.*\"resetRedraw\":true")
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"updateFrame\":false.*\"tickDesignerLayout\":false.*\"tickMainLayout\":true.*\"drawCanvasStroke\":false.*\"drawVrUi\":false.*\"drawMainUi\":false.*\"resetRedraw\":true")
|
||||
|
||||
add_test(NAME pano_cli_plan_app_frame_resize_smoke
|
||||
COMMAND pano_cli plan-app-frame --resize-width 1024.9 --resize-height 512.1)
|
||||
set_tests_properties(pano_cli_plan_app_frame_resize_smoke PROPERTIES
|
||||
LABELS "app;integration;desktop-fast"
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"resize\":\\{\"width\":1024.9,\"height\":512.1,\"renderTargetWidth\":1024,\"renderTargetHeight\":512,\"recreateUiRenderTarget\":true,\"requestRedraw\":true\\}")
|
||||
|
||||
add_test(NAME pano_cli_plan_app_frame_rejects_bad_resize
|
||||
COMMAND pano_cli plan-app-frame --bad-resize)
|
||||
set_tests_properties(pano_cli_plan_app_frame_rejects_bad_resize PROPERTIES
|
||||
LABELS "app;integration;desktop-fast;fuzz"
|
||||
WILL_FAIL TRUE
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"message\":\"resize dimensions")
|
||||
|
||||
add_test(NAME pano_cli_plan_app_shutdown_smoke
|
||||
COMMAND pano_cli plan-app-shutdown)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "app_core/app_frame.h"
|
||||
#include "test_harness.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
|
||||
void initial_surface_matches_legacy_default(pp::tests::Harness& harness)
|
||||
@@ -63,6 +65,42 @@ void draw_plan_respects_vr_visibility_modes(pp::tests::Harness& harness)
|
||||
PP_EXPECT(harness, !vr_only.draw_main_ui);
|
||||
}
|
||||
|
||||
void tick_plan_selects_available_layouts(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto both = pp::app::plan_app_frame_tick(true, true);
|
||||
const auto main_only = pp::app::plan_app_frame_tick(false, true);
|
||||
const auto none = pp::app::plan_app_frame_tick(false, false);
|
||||
|
||||
PP_EXPECT(harness, both.tick_designer_layout);
|
||||
PP_EXPECT(harness, both.tick_main_layout);
|
||||
PP_EXPECT(harness, !main_only.tick_designer_layout);
|
||||
PP_EXPECT(harness, main_only.tick_main_layout);
|
||||
PP_EXPECT(harness, !none.tick_designer_layout);
|
||||
PP_EXPECT(harness, !none.tick_main_layout);
|
||||
}
|
||||
|
||||
void resize_plan_projects_render_target_and_redraw(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto plan = pp::app::plan_app_resize(1280.9F, 720.1F);
|
||||
|
||||
PP_EXPECT(harness, plan);
|
||||
if (plan) {
|
||||
PP_EXPECT(harness, plan.value().width == 1280.9F);
|
||||
PP_EXPECT(harness, plan.value().height == 720.1F);
|
||||
PP_EXPECT(harness, plan.value().render_target_width == 1280);
|
||||
PP_EXPECT(harness, plan.value().render_target_height == 720);
|
||||
PP_EXPECT(harness, plan.value().recreate_ui_render_target);
|
||||
PP_EXPECT(harness, plan.value().request_redraw);
|
||||
}
|
||||
}
|
||||
|
||||
void resize_plan_rejects_invalid_dimensions(pp::tests::Harness& harness)
|
||||
{
|
||||
PP_EXPECT(harness, !pp::app::plan_app_resize(0.0F, 720.0F));
|
||||
PP_EXPECT(harness, !pp::app::plan_app_resize(1280.0F, -1.0F));
|
||||
PP_EXPECT(harness, !pp::app::plan_app_resize(std::nanf(""), 720.0F));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
@@ -74,5 +112,8 @@ int main()
|
||||
harness.run("draw plan selects canvas and UI passes", draw_plan_selects_canvas_and_ui_passes);
|
||||
harness.run("draw plan skips missing canvas document", draw_plan_skips_missing_canvas_document);
|
||||
harness.run("draw plan respects VR visibility modes", draw_plan_respects_vr_visibility_modes);
|
||||
harness.run("tick plan selects available layouts", tick_plan_selects_available_layouts);
|
||||
harness.run("resize plan projects render target and redraw", resize_plan_projects_render_target_and_redraw);
|
||||
harness.run("resize plan rejects invalid dimensions", resize_plan_rejects_invalid_dimensions);
|
||||
return harness.finish();
|
||||
}
|
||||
|
||||
@@ -250,11 +250,16 @@ struct PlanAppStartupResourcesArgs {
|
||||
struct PlanAppFrameArgs {
|
||||
bool redraw = false;
|
||||
bool animate = false;
|
||||
bool has_designer_layout = true;
|
||||
bool has_main_layout = true;
|
||||
bool has_canvas_node = true;
|
||||
bool has_canvas_document = true;
|
||||
bool vr_active = false;
|
||||
bool ui_visible = true;
|
||||
bool vr_only = false;
|
||||
float resize_width = 1280.0F;
|
||||
float resize_height = 720.0F;
|
||||
bool bad_resize = false;
|
||||
};
|
||||
|
||||
struct PlanBrushPackageExportArgs {
|
||||
@@ -2022,7 +2027,7 @@ void print_help()
|
||||
<< " 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-startup [--run-counter N] [--auto-timelapse-disabled] [--vr-controllers-disabled] [--license-invalid]\n"
|
||||
<< " plan-app-startup-resources [--width N] [--height N] [--bad-size]\n"
|
||||
<< " plan-app-frame [--redraw] [--animate] [--no-canvas] [--no-canvas-document] [--vr-active] [--ui-hidden] [--vr-only]\n"
|
||||
<< " plan-app-frame [--redraw] [--animate] [--no-designer-layout] [--no-main-layout] [--no-canvas] [--no-canvas-document] [--vr-active] [--ui-hidden] [--vr-only] [--resize-width N] [--resize-height N] [--bad-resize]\n"
|
||||
<< " plan-app-shutdown\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-brush-package-import --kind abr|ppbr --path FILE\n"
|
||||
@@ -3740,6 +3745,10 @@ pp::foundation::Status parse_plan_app_frame_args(
|
||||
args.redraw = true;
|
||||
} else if (key == "--animate") {
|
||||
args.animate = true;
|
||||
} else if (key == "--no-designer-layout") {
|
||||
args.has_designer_layout = false;
|
||||
} else if (key == "--no-main-layout") {
|
||||
args.has_main_layout = false;
|
||||
} else if (key == "--no-canvas") {
|
||||
args.has_canvas_node = false;
|
||||
args.has_canvas_document = false;
|
||||
@@ -3751,6 +3760,26 @@ pp::foundation::Status parse_plan_app_frame_args(
|
||||
args.ui_visible = false;
|
||||
} else if (key == "--vr-only") {
|
||||
args.vr_only = true;
|
||||
} else if (key == "--resize-width") {
|
||||
if (i + 1 >= argc) {
|
||||
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||
}
|
||||
const auto value = parse_float_arg(argv[++i]);
|
||||
if (!value) {
|
||||
return value.status();
|
||||
}
|
||||
args.resize_width = value.value();
|
||||
} else if (key == "--resize-height") {
|
||||
if (i + 1 >= argc) {
|
||||
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||
}
|
||||
const auto value = parse_float_arg(argv[++i]);
|
||||
if (!value) {
|
||||
return value.status();
|
||||
}
|
||||
args.resize_height = value.value();
|
||||
} else if (key == "--bad-resize") {
|
||||
args.bad_resize = true;
|
||||
} else {
|
||||
return pp::foundation::Status::invalid_argument("unknown option");
|
||||
}
|
||||
@@ -3770,12 +3799,20 @@ int plan_app_frame(int argc, char** argv)
|
||||
|
||||
const auto surface = pp::app::plan_app_initial_surface();
|
||||
const auto update = pp::app::plan_app_frame_update(args.redraw, args.animate);
|
||||
const auto tick = pp::app::plan_app_frame_tick(args.has_designer_layout, args.has_main_layout);
|
||||
const auto draw = pp::app::plan_app_frame_draw(
|
||||
args.has_canvas_node,
|
||||
args.has_canvas_document,
|
||||
args.vr_active,
|
||||
args.ui_visible,
|
||||
args.vr_only);
|
||||
const auto resize = pp::app::plan_app_resize(
|
||||
args.bad_resize ? 0.0F : args.resize_width,
|
||||
args.bad_resize ? std::nanf("") : args.resize_height);
|
||||
if (!resize) {
|
||||
print_error("plan-app-frame", resize.status().message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::cout << "{\"ok\":true,\"command\":\"plan-app-frame\""
|
||||
<< ",\"surface\":{\"width\":" << surface.width
|
||||
@@ -3783,10 +3820,18 @@ int plan_app_frame(int argc, char** argv)
|
||||
<< "},\"update\":{\"updateFrame\":" << json_bool(update.update_frame)
|
||||
<< ",\"updateLayouts\":" << json_bool(update.update_layouts)
|
||||
<< ",\"refreshCanvasToolbar\":" << json_bool(update.refresh_canvas_toolbar)
|
||||
<< "},\"tick\":{\"tickDesignerLayout\":" << json_bool(tick.tick_designer_layout)
|
||||
<< ",\"tickMainLayout\":" << json_bool(tick.tick_main_layout)
|
||||
<< "},\"draw\":{\"drawCanvasStroke\":" << json_bool(draw.draw_canvas_stroke)
|
||||
<< ",\"drawVrUi\":" << json_bool(draw.draw_vr_ui)
|
||||
<< ",\"drawMainUi\":" << json_bool(draw.draw_main_ui)
|
||||
<< ",\"resetRedraw\":" << json_bool(draw.reset_redraw)
|
||||
<< "},\"resize\":{\"width\":" << resize.value().width
|
||||
<< ",\"height\":" << resize.value().height
|
||||
<< ",\"renderTargetWidth\":" << resize.value().render_target_width
|
||||
<< ",\"renderTargetHeight\":" << resize.value().render_target_height
|
||||
<< ",\"recreateUiRenderTarget\":" << json_bool(resize.value().recreate_ui_render_target)
|
||||
<< ",\"requestRedraw\":" << json_bool(resize.value().request_redraw)
|
||||
<< "}}\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user