Route app frame decisions through app core

This commit is contained in:
2026-06-05 06:08:39 +02:00
parent 678bf2dcd6
commit f7979be80f
11 changed files with 287 additions and 12 deletions

View File

@@ -240,6 +240,7 @@ target_link_libraries(pp_platform_api
add_library(pp_app_core STATIC add_library(pp_app_core STATIC
src/app_core/about_menu.h src/app_core/about_menu.h
src/app_core/app_frame.h
src/app_core/app_preferences.h src/app_core/app_preferences.h
src/app_core/app_status.h src/app_core/app_status.h
src/app_core/app_startup.h src/app_core/app_startup.h

View File

@@ -241,6 +241,11 @@ Known local toolchain state:
retained shader loading, asset initialization, layout creation, title updates, retained shader loading, asset initialization, layout creation, title updates,
UI render-target creation, recording startup, VR-controller state mutation, UI render-target creation, recording startup, VR-controller state mutation,
settings writes, and license-warning dialogs remain legacy execution. 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.
- `src/legacy_app_preference_services.*` is the current app-shell bridge for - `src/legacy_app_preference_services.*` is the current app-shell bridge for
options-menu preference execution. It keeps UI scale, viewport scale, RTL, options-menu preference execution. It keeps UI scale, viewport scale, RTL,
VR mode, VR-controller, auto-timelapse, and canvas cursor-mode callbacks on VR mode, VR-controller, auto-timelapse, and canvas cursor-mode callbacks on
@@ -824,6 +829,9 @@ Known local toolchain state:
planning, optional auto-timelapse/license/VR-controller decisions, negative planning, optional auto-timelapse/license/VR-controller decisions, negative
and overflow run-counter rejection, stable full startup dispatch ordering, and overflow run-counter rejection, stable full startup dispatch ordering,
split persistence/runtime dispatch, and malformed startup-plan rejection. 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.
- `pp_platform_api_tests` covers service dispatch for clipboard read/write, - `pp_platform_api_tests` covers service dispatch for clipboard read/write,
empty clipboard writes, cursor visibility, virtual-keyboard visibility, empty clipboard writes, cursor visibility, virtual-keyboard visibility,
external file display, file sharing, VR lifecycle, layout/asset file load external file display, file sharing, VR lifecycle, layout/asset file load

View File

@@ -20,7 +20,7 @@ agent or engineer to remove them without reconstructing context from chat.
- 2026-06-04: DEBT-0009 was narrowed. `platform-build.ps1` and - 2026-06-04: DEBT-0009 was narrowed. `platform-build.ps1` and
`platform-build.sh` now include the current headless component/test matrix, `platform-build.sh` now include the current headless component/test matrix,
including brush-package coverage and the app-core startup/file/document/ including brush-package coverage and the app-core startup/frame/file/document/
brush/canvas/history/grid/toolbar/tools/about/preferences/status automation brush/canvas/history/grid/toolbar/tools/about/preferences/status automation
tests, and `panopainter_platform_build_target_matrix_self_test` now verifies tests, and `panopainter_platform_build_target_matrix_self_test` now verifies
the wrapper defaults against the current CMake test executables. On the wrapper defaults against the current CMake test executables. On
@@ -91,6 +91,12 @@ agent or engineer to remove them without reconstructing context from chat.
the retained OpenGL startup task in place, then delegates startup resources the retained OpenGL startup task in place, then delegates startup resources
and runtime side effects through the startup bridge. `pano_cli and runtime side effects through the startup bridge. `pano_cli
plan-app-startup-resources` exposes the resource path for automation. 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
`pano_cli plan-app-frame`; retained layout traversal, toolbar widget writes,
and OpenGL/UI drawing remain in the legacy app.
- 2026-06-04: DEBT-0036 was narrowed again. Canvas stroke commit, - 2026-06-04: DEBT-0036 was narrowed again. Canvas stroke commit,
thumbnail, and object-draw history paths now query saved blend state through thumbnail, and object-draw history paths now query saved blend state through
tested `pp_renderer_gl` capability-state dispatch; CanvasLayer equirect tested `pp_renderer_gl` capability-state dispatch; CanvasLayer equirect

View File

@@ -198,6 +198,11 @@ and floating-point render targets; `App::title_update`,
`App::update_memory_usage`, `App::update_rec_frames`, resolution helpers, `App::update_memory_usage`, `App::update_rec_frames`, resolution helpers,
`App::initLayout`, and `pano_cli plan-app-status` consume those contracts while `App::initLayout`, and `pano_cli plan-app-status` consume those contracts while
legacy UI nodes still render the strings and status lights. 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.
`panopainter_app` is now a real static target that owns app orchestration `panopainter_app` is now a real static target that owns app orchestration
sources, app version metadata, and version-header generation. sources, app version metadata, and version-header generation.
`pp_panopainter_ui` now owns app-specific modal, dialog, panel, canvas, `pp_panopainter_ui` now owns app-specific modal, dialog, panel, canvas,
@@ -1286,7 +1291,7 @@ standard x64/arm64, Android Quest arm64, Android Focus/Wave arm64,
Emscripten/WebGL, macOS, iOS device, and iOS simulator. `platform-build` Emscripten/WebGL, macOS, iOS device, and iOS simulator. `platform-build`
automation now builds the current headless component matrix, including automation now builds the current headless component matrix, including
`pp_platform_api`, `pp_app_core`, platform API tests, brush-package tests, and `pp_platform_api`, `pp_app_core`, platform API tests, brush-package tests, and
the current app-core startup/file/document/brush/canvas/history/grid/toolbar/ the current app-core startup/frame/file/document/brush/canvas/history/grid/toolbar/
tools/about/preferences/status automation tests. The PowerShell wrapper also tools/about/preferences/status automation tests. The PowerShell wrapper also
normalizes comma-separated `-Presets` and `-Targets` values for reliable normalizes comma-separated `-Presets` and `-Targets` values for reliable
machine-driven partial matrix checks. `panopainter_platform_build_target_matrix_self_test` machine-driven partial matrix checks. `panopainter_platform_build_target_matrix_self_test`
@@ -1643,6 +1648,11 @@ Results:
`pano_cli_plan_app_startup_rejects_negative_counter`, with startup resource `pano_cli_plan_app_startup_rejects_negative_counter`, with startup resource
sequencing also covered by `pano_cli_plan_app_startup_resources_smoke` and sequencing also covered by `pano_cli_plan_app_startup_resources_smoke` and
`pano_cli_plan_app_startup_resources_rejects_bad_size`. `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`.
- 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`.
- `PanoPainter`, `pp_app_core_brush_package_export_tests`, and `pano_cli` built - `PanoPainter`, `pp_app_core_brush_package_export_tests`, and `pano_cli` built
after PPBR brush package export request validation and dispatch moved behind after PPBR brush package export request validation and dispatch moved behind
app-core brush package services. app-core brush package services.

View File

@@ -42,6 +42,7 @@ param(
"pp_ui_core_layout_xml_tests", "pp_ui_core_layout_xml_tests",
"pp_app_core_about_menu_tests", "pp_app_core_about_menu_tests",
"pp_app_core_app_preferences_tests", "pp_app_core_app_preferences_tests",
"pp_app_core_app_frame_tests",
"pp_app_core_app_startup_tests", "pp_app_core_app_startup_tests",
"pp_app_core_app_status_tests", "pp_app_core_app_status_tests",
"pp_app_core_brush_package_export_tests", "pp_app_core_brush_package_export_tests",
@@ -49,6 +50,7 @@ param(
"pp_app_core_brush_ui_tests", "pp_app_core_brush_ui_tests",
"pp_app_core_canvas_hotkey_tests", "pp_app_core_canvas_hotkey_tests",
"pp_app_core_canvas_tool_ui_tests", "pp_app_core_canvas_tool_ui_tests",
"pp_app_core_canvas_view_tests",
"pp_app_core_document_animation_tests", "pp_app_core_document_animation_tests",
"pp_app_core_document_canvas_tests", "pp_app_core_document_canvas_tests",
"pp_app_core_document_cloud_tests", "pp_app_core_document_cloud_tests",

View File

@@ -3,7 +3,7 @@ set -u
presets="${1:-android-arm64 android-x64 android-quest-arm64 android-focus-arm64}" presets="${1:-android-arm64 android-x64 android-quest-arm64 android-focus-arm64}"
shift || true shift || true
targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_renderer_gl pp_paint_renderer pp_ui_core pp_platform_api pp_app_core pano_cli pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_brush_package_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_paint_stroke_script_tests pp_document_tests pp_document_ppi_import_tests pp_document_ppi_export_tests pp_renderer_api_tests pp_renderer_gl_capabilities_tests pp_renderer_gl_command_plan_tests pp_paint_renderer_compositor_tests pp_platform_api_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pp_app_core_about_menu_tests pp_app_core_app_preferences_tests pp_app_core_app_startup_tests pp_app_core_app_status_tests pp_app_core_brush_package_export_tests pp_app_core_brush_package_import_tests pp_app_core_brush_ui_tests pp_app_core_canvas_hotkey_tests pp_app_core_canvas_tool_ui_tests pp_app_core_document_animation_tests pp_app_core_document_canvas_tests pp_app_core_document_cloud_tests pp_app_core_document_export_tests pp_app_core_document_import_tests pp_app_core_document_layer_tests pp_app_core_document_platform_io_tests pp_app_core_document_recording_tests pp_app_core_document_resize_tests pp_app_core_document_route_tests pp_app_core_document_sharing_tests pp_app_core_document_session_tests pp_app_core_file_menu_tests pp_app_core_grid_ui_tests pp_app_core_history_ui_tests pp_app_core_main_toolbar_tests pp_app_core_quick_ui_tests pp_app_core_tools_menu_tests}" targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_renderer_gl pp_paint_renderer pp_ui_core pp_platform_api pp_app_core pano_cli pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_brush_package_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_paint_stroke_script_tests pp_document_tests pp_document_ppi_import_tests pp_document_ppi_export_tests pp_renderer_api_tests pp_renderer_gl_capabilities_tests pp_renderer_gl_command_plan_tests pp_paint_renderer_compositor_tests pp_platform_api_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pp_app_core_about_menu_tests pp_app_core_app_preferences_tests pp_app_core_app_frame_tests pp_app_core_app_startup_tests pp_app_core_app_status_tests pp_app_core_brush_package_export_tests pp_app_core_brush_package_import_tests pp_app_core_brush_ui_tests pp_app_core_canvas_hotkey_tests pp_app_core_canvas_tool_ui_tests pp_app_core_canvas_view_tests pp_app_core_document_animation_tests pp_app_core_document_canvas_tests pp_app_core_document_cloud_tests pp_app_core_document_export_tests pp_app_core_document_import_tests pp_app_core_document_layer_tests pp_app_core_document_platform_io_tests pp_app_core_document_recording_tests pp_app_core_document_resize_tests pp_app_core_document_route_tests pp_app_core_document_sharing_tests pp_app_core_document_session_tests pp_app_core_file_menu_tests pp_app_core_grid_ui_tests pp_app_core_history_ui_tests pp_app_core_main_toolbar_tests pp_app_core_quick_ui_tests pp_app_core_tools_menu_tests}"
start="$(date +%s)" start="$(date +%s)"
overall_exit=0 overall_exit=0

View File

@@ -5,6 +5,7 @@
#include "node_dialog_open.h" #include "node_dialog_open.h"
#include "node_progress_bar.h" #include "node_progress_bar.h"
#include "mp4enc.h" #include "mp4enc.h"
#include "app_core/app_frame.h"
#include "app_core/app_status.h" #include "app_core/app_status.h"
#include "app_core/app_startup.h" #include "app_core/app_startup.h"
#include "app_core/canvas_tool_ui.h" #include "app_core/canvas_tool_ui.h"
@@ -182,8 +183,9 @@ bool App::ui_running = false;
void App::create() void App::create()
{ {
width = 1920/2; const auto initial_surface = pp::app::plan_app_initial_surface();
height = 1080/2; width = initial_surface.width;
height = initial_surface.height;
} }
void App::open_document(std::string path) void App::open_document(std::string path)
@@ -540,13 +542,20 @@ bool App::update_ui_observer(Node *n)
void App::draw(float dt) void App::draw(float dt)
{ {
const auto draw_plan = pp::app::plan_app_frame_draw(
canvas != nullptr,
canvas && canvas->m_canvas,
vr_active,
ui_visible,
vr_only);
// update offscreen stuff // update offscreen stuff
if (canvas && canvas->m_canvas) if (draw_plan.draw_canvas_stroke)
canvas->m_canvas->stroke_draw(); canvas->m_canvas->stroke_draw();
auto observer = std::bind(&App::update_ui_observer, this, std::placeholders::_1); auto observer = std::bind(&App::update_ui_observer, this, std::placeholders::_1);
if (vr_active && ui_visible) if (draw_plan.draw_vr_ui)
{ {
uirtt.bindFramebuffer(); uirtt.bindFramebuffer();
uirtt.clear(); uirtt.clear();
@@ -564,7 +573,7 @@ void App::draw(float dt)
uirtt.unbindFramebuffer(); uirtt.unbindFramebuffer();
} }
if (!vr_only) if (draw_plan.draw_main_ui)
{ {
bind_main_render_target(); bind_main_render_target();
apply_app_viewport(pp::renderer::gl::OpenGlViewportRect { apply_app_viewport(pp::renderer::gl::OpenGlViewportRect {
@@ -582,6 +591,7 @@ void App::draw(float dt)
apply_app_scissor_test(false); apply_app_scissor_test(false);
} }
if (draw_plan.reset_redraw)
redraw = false; redraw = false;
} }
@@ -592,15 +602,19 @@ void App::update(float dt)
// avoid multiple threads to update the scene // avoid multiple threads to update the scene
//std::lock_guard<std::mutex> lock(mutex); //std::lock_guard<std::mutex> lock(mutex);
if (!(redraw || animate)) const auto update_plan = pp::app::plan_app_frame_update(redraw, animate);
if (!update_plan.update_frame)
return; return;
if (auto* main = layout[main_id]) if (auto* main = layout[main_id]; update_plan.update_layouts && main)
main->update(width, height, zoom); main->update(width, height, zoom);
if (auto* main = layout_designer[main_id]) if (auto* main = layout_designer[main_id]; update_plan.update_layouts && main)
main->update(width, height, zoom); main->update(width, height, zoom);
if (!update_plan.refresh_canvas_toolbar)
return;
{ {
auto mode = Canvas::I->m_current_mode; auto mode = Canvas::I->m_current_mode;

56
src/app_core/app_frame.h Normal file
View File

@@ -0,0 +1,56 @@
#pragma once
namespace pp::app {
struct AppInitialSurfacePlan {
float width = 960.0F;
float height = 540.0F;
};
struct AppFrameUpdatePlan {
bool update_frame = false;
bool update_layouts = false;
bool refresh_canvas_toolbar = false;
};
struct AppFrameDrawPlan {
bool draw_canvas_stroke = false;
bool draw_vr_ui = false;
bool draw_main_ui = true;
bool reset_redraw = true;
};
[[nodiscard]] constexpr AppInitialSurfacePlan plan_app_initial_surface() noexcept
{
return AppInitialSurfacePlan {
.width = 1920.0F / 2.0F,
.height = 1080.0F / 2.0F,
};
}
[[nodiscard]] constexpr AppFrameUpdatePlan plan_app_frame_update(bool redraw, bool animate) noexcept
{
const bool update_frame = redraw || animate;
return AppFrameUpdatePlan {
.update_frame = update_frame,
.update_layouts = update_frame,
.refresh_canvas_toolbar = update_frame,
};
}
[[nodiscard]] constexpr AppFrameDrawPlan plan_app_frame_draw(
bool has_canvas_node,
bool has_canvas_document,
bool vr_active,
bool ui_visible,
bool vr_only) noexcept
{
return AppFrameDrawPlan {
.draw_canvas_stroke = has_canvas_node && has_canvas_document,
.draw_vr_ui = vr_active && ui_visible,
.draw_main_ui = !vr_only,
.reset_redraw = true,
};
}
} // namespace pp::app

View File

@@ -555,6 +555,16 @@ add_test(NAME pp_app_core_app_startup_tests COMMAND pp_app_core_app_startup_test
set_tests_properties(pp_app_core_app_startup_tests PROPERTIES set_tests_properties(pp_app_core_app_startup_tests PROPERTIES
LABELS "app;desktop-fast;fuzz") LABELS "app;desktop-fast;fuzz")
add_executable(pp_app_core_app_frame_tests
app_core/app_frame_tests.cpp)
target_link_libraries(pp_app_core_app_frame_tests PRIVATE
pp_app_core
pp_test_harness)
add_test(NAME pp_app_core_app_frame_tests COMMAND pp_app_core_app_frame_tests)
set_tests_properties(pp_app_core_app_frame_tests PROPERTIES
LABELS "app;desktop-fast;fuzz")
add_executable(pp_app_core_document_sharing_tests add_executable(pp_app_core_document_sharing_tests
app_core/document_sharing_tests.cpp) app_core/document_sharing_tests.cpp)
target_link_libraries(pp_app_core_document_sharing_tests PRIVATE target_link_libraries(pp_app_core_document_sharing_tests PRIVATE
@@ -947,6 +957,18 @@ if(TARGET pano_cli)
WILL_FAIL TRUE WILL_FAIL TRUE
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-startup-resources\".*\"message\":\"startup resource dimensions") PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-startup-resources\".*\"message\":\"startup resource dimensions")
add_test(NAME pano_cli_plan_app_frame_vr_smoke
COMMAND pano_cli plan-app-frame --redraw --vr-active)
set_tests_properties(pano_cli_plan_app_frame_vr_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
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)
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")
add_test(NAME pano_cli_plan_brush_package_import_ppbr_smoke add_test(NAME pano_cli_plan_brush_package_import_ppbr_smoke
COMMAND pano_cli plan-brush-package-import COMMAND pano_cli plan-brush-package-import
--kind ppbr --kind ppbr

View File

@@ -0,0 +1,78 @@
#include "app_core/app_frame.h"
#include "test_harness.h"
namespace {
void initial_surface_matches_legacy_default(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_app_initial_surface();
PP_EXPECT(harness, plan.width == 960.0F);
PP_EXPECT(harness, plan.height == 540.0F);
}
void update_plan_skips_idle_frames(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_app_frame_update(false, false);
PP_EXPECT(harness, !plan.update_frame);
PP_EXPECT(harness, !plan.update_layouts);
PP_EXPECT(harness, !plan.refresh_canvas_toolbar);
}
void update_plan_refreshes_redraw_or_animated_frames(pp::tests::Harness& harness)
{
const auto redraw = pp::app::plan_app_frame_update(true, false);
const auto animated = pp::app::plan_app_frame_update(false, true);
PP_EXPECT(harness, redraw.update_frame);
PP_EXPECT(harness, redraw.update_layouts);
PP_EXPECT(harness, redraw.refresh_canvas_toolbar);
PP_EXPECT(harness, animated.update_frame);
PP_EXPECT(harness, animated.update_layouts);
PP_EXPECT(harness, animated.refresh_canvas_toolbar);
}
void draw_plan_selects_canvas_and_ui_passes(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_app_frame_draw(true, true, true, true, false);
PP_EXPECT(harness, plan.draw_canvas_stroke);
PP_EXPECT(harness, plan.draw_vr_ui);
PP_EXPECT(harness, plan.draw_main_ui);
PP_EXPECT(harness, plan.reset_redraw);
}
void draw_plan_skips_missing_canvas_document(pp::tests::Harness& harness)
{
const auto missing_node = pp::app::plan_app_frame_draw(false, true, false, true, false);
const auto missing_document = pp::app::plan_app_frame_draw(true, false, false, true, false);
PP_EXPECT(harness, !missing_node.draw_canvas_stroke);
PP_EXPECT(harness, !missing_document.draw_canvas_stroke);
}
void draw_plan_respects_vr_visibility_modes(pp::tests::Harness& harness)
{
const auto hidden_vr_ui = pp::app::plan_app_frame_draw(true, true, true, false, false);
const auto vr_only = pp::app::plan_app_frame_draw(true, true, false, true, true);
PP_EXPECT(harness, !hidden_vr_ui.draw_vr_ui);
PP_EXPECT(harness, hidden_vr_ui.draw_main_ui);
PP_EXPECT(harness, !vr_only.draw_vr_ui);
PP_EXPECT(harness, !vr_only.draw_main_ui);
}
} // namespace
int main()
{
pp::tests::Harness harness;
harness.run("initial surface matches legacy default", initial_surface_matches_legacy_default);
harness.run("update plan skips idle frames", update_plan_skips_idle_frames);
harness.run("update plan refreshes redraw or animated frames", update_plan_refreshes_redraw_or_animated_frames);
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);
return harness.finish();
}

View File

@@ -1,5 +1,6 @@
#include "app_core/about_menu.h" #include "app_core/about_menu.h"
#include "app_core/app_preferences.h" #include "app_core/app_preferences.h"
#include "app_core/app_frame.h"
#include "app_core/app_status.h" #include "app_core/app_status.h"
#include "app_core/app_startup.h" #include "app_core/app_startup.h"
#include "app_core/brush_package_import.h" #include "app_core/brush_package_import.h"
@@ -245,6 +246,16 @@ struct PlanAppStartupResourcesArgs {
bool bad_size = false; bool bad_size = false;
}; };
struct PlanAppFrameArgs {
bool redraw = false;
bool animate = false;
bool has_canvas_node = true;
bool has_canvas_document = true;
bool vr_active = false;
bool ui_visible = true;
bool vr_only = false;
};
struct PlanBrushPackageExportArgs { struct PlanBrushPackageExportArgs {
std::string path; std::string path;
std::string author; std::string author;
@@ -2010,6 +2021,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-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 [--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-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-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-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" << " plan-brush-package-import --kind abr|ppbr --path FILE\n"
<< " plan-brush-package-export --path FILE [--author NAME] [--email EMAIL] [--url URL] [--description TEXT] [--dest-path DIR] [--export-data|--no-export-data] [--header-image]\n" << " plan-brush-package-export --path FILE [--author NAME] [--email EMAIL] [--url URL] [--description TEXT] [--dest-path DIR] [--export-data|--no-export-data] [--header-image]\n"
@@ -3715,6 +3727,68 @@ int plan_app_startup_resources(int argc, char** argv)
return 0; return 0;
} }
pp::foundation::Status parse_plan_app_frame_args(
int argc,
char** argv,
PlanAppFrameArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--redraw") {
args.redraw = true;
} else if (key == "--animate") {
args.animate = true;
} else if (key == "--no-canvas") {
args.has_canvas_node = false;
args.has_canvas_document = false;
} else if (key == "--no-canvas-document") {
args.has_canvas_document = false;
} else if (key == "--vr-active") {
args.vr_active = true;
} else if (key == "--ui-hidden") {
args.ui_visible = false;
} else if (key == "--vr-only") {
args.vr_only = true;
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
return pp::foundation::Status::success();
}
int plan_app_frame(int argc, char** argv)
{
PlanAppFrameArgs args;
const auto status = parse_plan_app_frame_args(argc, argv, args);
if (!status.ok()) {
print_error("plan-app-frame", status.message);
return 2;
}
const auto surface = pp::app::plan_app_initial_surface();
const auto update = pp::app::plan_app_frame_update(args.redraw, args.animate);
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);
std::cout << "{\"ok\":true,\"command\":\"plan-app-frame\""
<< ",\"surface\":{\"width\":" << surface.width
<< ",\"height\":" << surface.height
<< "},\"update\":{\"updateFrame\":" << json_bool(update.update_frame)
<< ",\"updateLayouts\":" << json_bool(update.update_layouts)
<< ",\"refreshCanvasToolbar\":" << json_bool(update.refresh_canvas_toolbar)
<< "},\"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)
<< "}}\n";
return 0;
}
pp::foundation::Status parse_plan_brush_package_import_args( pp::foundation::Status parse_plan_brush_package_import_args(
int argc, int argc,
char** argv, char** argv,
@@ -9962,6 +10036,10 @@ int main(int argc, char** argv)
return plan_app_startup_resources(argc, argv); return plan_app_startup_resources(argc, argv);
} }
if (command == "plan-app-frame") {
return plan_app_frame(argc, argv);
}
if (command == "plan-brush-package-import") { if (command == "plan-brush-package-import") {
return plan_brush_package_import(argc, argv); return plan_brush_package_import(argc, argv);
} }