Route render capture hooks through platform services

This commit is contained in:
2026-06-03 04:54:29 +02:00
parent 7a9b14a86f
commit beb7f717f1
10 changed files with 76 additions and 18 deletions

View File

@@ -447,8 +447,9 @@ Known local toolchain state:
- `pp_platform_api` exposes the SDK-free `PlatformServices` interface for - `pp_platform_api` exposes the SDK-free `PlatformServices` interface for
clipboard text, cursor visibility, virtual-keyboard visibility, external clipboard text, cursor visibility, virtual-keyboard visibility, external
file display, file sharing, native app/window close, UI-thread lifecycle file display, file sharing, native app/window close, UI-thread lifecycle
hooks, render-context lifecycle hooks, per-frame platform hooks, picker hooks, render-context lifecycle hooks, render-capture frame hooks, per-frame
callbacks, and prepared-file save/download handoff; Windows platform hooks, picker callbacks, and prepared-file save/download handoff;
Windows
live app execution now uses injected live app execution now uses injected
`WindowsPlatformServices` from `WindowsPlatformServices` from
`src/platform_windows/windows_platform_services.*` in `pp_platform_windows`, `src/platform_windows/windows_platform_services.*` in `pp_platform_windows`,

View File

@@ -35,7 +35,7 @@ agent or engineer to remove them without reconstructing context from chat.
| DEBT-0014 | Open | Modernization | `windows-clangcl-asan` now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer | Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible | `cmake --fresh --preset windows-clangcl-asan`; `cmake --build --preset windows-clangcl-asan --target pp_foundation` | Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make `platform-build.ps1 -Presets windows-clangcl-asan` pass for the headless matrix | | DEBT-0014 | Open | Modernization | `windows-clangcl-asan` now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer | Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible | `cmake --fresh --preset windows-clangcl-asan`; `cmake --build --preset windows-clangcl-asan --target pp_foundation` | Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make `platform-build.ps1 -Presets windows-clangcl-asan` pass for the headless matrix |
| DEBT-0015 | Open | Modernization | Cursor visibility requests now consume pure `pp_app_core` planning through `pano_cli plan-cursor-visibility`, `App::show_cursor`/`App::hide_cursor` dispatch through `PlatformServices` without platform guards, and Windows live execution uses injected `WindowsPlatformServices`, but macOS cursor execution still reaches the retained fallback adapter | Keep canvas cursor behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-cursor-visibility --visible`; `ctest --preset desktop-fast --build-config Debug` | Cursor visibility execution is owned by injected `pp_platform_*` services for every supported platform | | DEBT-0015 | Open | Modernization | Cursor visibility requests now consume pure `pp_app_core` planning through `pano_cli plan-cursor-visibility`, `App::show_cursor`/`App::hide_cursor` dispatch through `PlatformServices` without platform guards, and Windows live execution uses injected `WindowsPlatformServices`, but macOS cursor execution still reaches the retained fallback adapter | Keep canvas cursor behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-cursor-visibility --visible`; `ctest --preset desktop-fast --build-config Debug` | Cursor visibility execution is owned by injected `pp_platform_*` services for every supported platform |
| DEBT-0016 | Open | Modernization | Clipboard get/set requests now consume pure `pp_app_core` planning through `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write`, and Windows live execution uses injected `WindowsPlatformServices`, but Apple/Android clipboard execution still reaches retained fallback adapter branches from `App::clipboard_get_text` and `App::clipboard_set_text` | Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-clipboard-write --text #ff00aa`; `ctest --preset desktop-fast --build-config Debug` | Clipboard execution is owned by injected `pp_platform_*` services for every supported platform | | DEBT-0016 | Open | Modernization | Clipboard get/set requests now consume pure `pp_app_core` planning through `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write`, and Windows live execution uses injected `WindowsPlatformServices`, but Apple/Android clipboard execution still reaches retained fallback adapter branches from `App::clipboard_get_text` and `App::clipboard_set_text` | Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-clipboard-write --text #ff00aa`; `ctest --preset desktop-fast --build-config Debug` | Clipboard execution is owned by injected `pp_platform_*` services for every supported platform |
| DEBT-0017 | Open | Modernization | `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, and prepared-file save/download handoff now call the SDK-free `pp::platform::PlatformServices` interface, and Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`; non-Windows live implementations still use `src/platform_legacy/legacy_platform_services.*`, a named fallback adapter that forwards to retained Apple/Android/Linux/Web bridge functions and retained no-op branches | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell | | DEBT-0017 | Open | Modernization | `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-capture frame hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, and prepared-file save/download handoff now call the SDK-free `pp::platform::PlatformServices` interface, and Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`; non-Windows live implementations still use `src/platform_legacy/legacy_platform_services.*`, a named fallback adapter that forwards to retained Apple/Android/Linux/Web bridge functions and retained no-op branches | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell |
## Closed Debt ## Closed Debt

View File

@@ -470,8 +470,9 @@ app-core clipboard text decisions used by live clipboard get/set requests
before retained platform clipboard bridges continue. before retained platform clipboard bridges continue.
`pp_platform_api` now owns a headless `PlatformServices` interface for `pp_platform_api` now owns a headless `PlatformServices` interface for
clipboard text, cursor visibility, virtual-keyboard visibility, UI-thread clipboard text, cursor visibility, virtual-keyboard visibility, UI-thread
lifecycle hooks, render-context acquire/release/present hooks, external file lifecycle hooks, render-context acquire/release/present hooks, render-capture
display, file sharing, image/file/save-file pickers, and directory pickers. frame hooks, external file display, file sharing, image/file/save-file
pickers, and directory pickers.
Windows installs an injected `WindowsPlatformServices` implementation from Windows installs an injected `WindowsPlatformServices` implementation from
`src/platform_windows/windows_platform_services.*` in `pp_platform_windows`; `src/platform_windows/windows_platform_services.*` in `pp_platform_windows`;
other platforms still route through the debt-tracked legacy fallback adapter other platforms still route through the debt-tracked legacy fallback adapter
@@ -500,6 +501,9 @@ The app's render context acquire/release/present path now dispatches through
rebinding, and swap in `WindowsPlatformServices`; Apple, Android, Linux, and rebinding, and swap in `WindowsPlatformServices`; Apple, Android, Linux, and
WebGL behavior is preserved behind the legacy adapter until their platform WebGL behavior is preserved behind the legacy adapter until their platform
shells are injected. shells are injected.
Windows RenderDoc frame capture hooks now also dispatch through
`PlatformServices`, keeping capture integration in the platform service while
leaving non-Windows adapters as no-ops.
`pano_cli plan-cloud-upload` exposes the app-core cloud upload decision used by `pano_cli plan-cloud-upload` exposes the app-core cloud upload decision used by
the live cloud upload command for missing-canvas, new-document warning, publish the live cloud upload command for missing-canvas, new-document warning, publish
prompt, and dirty-document save-before-upload states before legacy UI, canvas, prompt, and dirty-document save-before-upload states before legacy UI, canvas,
@@ -1014,9 +1018,10 @@ Results:
interface for clipboard read/write, empty clipboard writes, cursor interface for clipboard read/write, empty clipboard writes, cursor
visibility dispatch, virtual-keyboard visibility dispatch, external file visibility dispatch, virtual-keyboard visibility dispatch, external file
display dispatch, file sharing dispatch, native app/window close dispatch, display dispatch, file sharing dispatch, native app/window close dispatch,
UI-thread lifecycle dispatch, render-context lifecycle dispatch, per-frame UI-thread lifecycle dispatch, render-context lifecycle dispatch,
platform hook dispatch, picker callback dispatch, and prepared-file render-capture frame hook dispatch, per-frame platform hook dispatch, picker
save/download callback dispatch. The live Windows app now callback dispatch, and prepared-file save/download callback dispatch. The
live Windows app now
consumes this interface through an injected consumes this interface through an injected
`WindowsPlatformServices` instance isolated in `WindowsPlatformServices` instance isolated in
`src/platform_windows/windows_platform_services.*`; other platforms still `src/platform_windows/windows_platform_services.*`; other platforms still

View File

@@ -16,10 +16,7 @@
#endif #endif
#include "settings.h" #include "settings.h"
#ifdef _WIN32 #ifdef __LINUX__
void win32_renderdoc_frame_start();
void win32_renderdoc_frame_end();
#elif __LINUX__
std::string linux_home_path(); std::string linux_home_path();
int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE); int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE);
#elif __WEB__ #elif __WEB__
@@ -818,16 +815,12 @@ std::string App::res_to_string(int res)
void App::renderdoc_frame_start() void App::renderdoc_frame_start()
{ {
#if __WIN__ begin_render_capture_frame();
win32_renderdoc_frame_start();
#endif
} }
void App::renderdoc_frame_end() void App::renderdoc_frame_end()
{ {
#if __WIN__ end_render_capture_frame();
win32_renderdoc_frame_end();
#endif
} }
void App::rec_clear() void App::rec_clear()

View File

@@ -195,6 +195,8 @@ public:
void acquire_render_context(); void acquire_render_context();
void release_render_context(); void release_render_context();
void present_render_context(); void present_render_context();
void begin_render_capture_frame();
void end_render_capture_frame();
void update_platform_frame(float delta_time_seconds); void update_platform_frame(float delta_time_seconds);
void report_rendered_frames(int frames); void report_rendered_frames(int frames);
void save_prepared_file( void save_prepared_file(

View File

@@ -244,6 +244,16 @@ void App::present_render_context()
active_platform_services().present_render_context(); active_platform_services().present_render_context();
} }
void App::begin_render_capture_frame()
{
active_platform_services().begin_render_capture_frame();
}
void App::end_render_capture_frame()
{
active_platform_services().end_render_capture_frame();
}
void App::update_platform_frame(float delta_time_seconds) void App::update_platform_frame(float delta_time_seconds)
{ {
active_platform_services().update_platform_frame(delta_time_seconds); active_platform_services().update_platform_frame(delta_time_seconds);

View File

@@ -23,6 +23,8 @@ public:
virtual void acquire_render_context() = 0; virtual void acquire_render_context() = 0;
virtual void release_render_context() = 0; virtual void release_render_context() = 0;
virtual void present_render_context() = 0; virtual void present_render_context() = 0;
virtual void begin_render_capture_frame() = 0;
virtual void end_render_capture_frame() = 0;
virtual void update_platform_frame(float delta_time_seconds) = 0; virtual void update_platform_frame(float delta_time_seconds) = 0;
virtual void report_rendered_frames(int frames) = 0; virtual void report_rendered_frames(int frames) = 0;
virtual void display_file(std::string_view path) = 0; virtual void display_file(std::string_view path) = 0;

View File

@@ -144,6 +144,14 @@ public:
#endif #endif
} }
void begin_render_capture_frame() override
{
}
void end_render_capture_frame() override
{
}
void update_platform_frame(float delta_time_seconds) override void update_platform_frame(float delta_time_seconds) override
{ {
(void)delta_time_seconds; (void)delta_time_seconds;

View File

@@ -13,6 +13,8 @@ void destroy_window();
void async_lock(); void async_lock();
void async_unlock(); void async_unlock();
void win32_async_swap(); void win32_async_swap();
void win32_renderdoc_frame_start();
void win32_renderdoc_frame_end();
void win32_update_fps(int frames); void win32_update_fps(int frames);
void win32_update_stylus(float dt); void win32_update_stylus(float dt);
@@ -201,6 +203,16 @@ public:
win32_async_swap(); win32_async_swap();
} }
void begin_render_capture_frame() override
{
win32_renderdoc_frame_start();
}
void end_render_capture_frame() override
{
win32_renderdoc_frame_end();
}
void update_platform_frame(float delta_time_seconds) override void update_platform_frame(float delta_time_seconds) override
{ {
win32_update_stylus(delta_time_seconds); win32_update_stylus(delta_time_seconds);

View File

@@ -65,6 +65,16 @@ public:
++render_context_presents; ++render_context_presents;
} }
void begin_render_capture_frame() override
{
++render_capture_begins;
}
void end_render_capture_frame() override
{
++render_capture_ends;
}
void update_platform_frame(float delta_time_seconds) override void update_platform_frame(float delta_time_seconds) override
{ {
++platform_frame_updates; ++platform_frame_updates;
@@ -140,6 +150,8 @@ public:
int render_context_acquires = 0; int render_context_acquires = 0;
int render_context_releases = 0; int render_context_releases = 0;
int render_context_presents = 0; int render_context_presents = 0;
int render_capture_begins = 0;
int render_capture_ends = 0;
int platform_frame_updates = 0; int platform_frame_updates = 0;
int frame_reports = 0; int frame_reports = 0;
int display_file_requests = 0; int display_file_requests = 0;
@@ -233,6 +245,18 @@ void platform_services_dispatch_render_context_lifecycle(pp::tests::Harness& har
PP_EXPECT(harness, fake.render_context_releases == 1); PP_EXPECT(harness, fake.render_context_releases == 1);
} }
void platform_services_dispatch_render_capture_hooks(pp::tests::Harness& harness)
{
FakePlatformServices fake("unused");
pp::platform::PlatformServices& services = fake;
services.begin_render_capture_frame();
services.end_render_capture_frame();
PP_EXPECT(harness, fake.render_capture_begins == 1);
PP_EXPECT(harness, fake.render_capture_ends == 1);
}
void platform_services_dispatch_frame_hooks(pp::tests::Harness& harness) void platform_services_dispatch_frame_hooks(pp::tests::Harness& harness)
{ {
FakePlatformServices fake("unused"); FakePlatformServices fake("unused");
@@ -321,6 +345,7 @@ int main()
harness.run("platform services dispatch visibility updates", platform_services_dispatch_visibility_updates); harness.run("platform services dispatch visibility updates", platform_services_dispatch_visibility_updates);
harness.run("platform services dispatch UI thread lifecycle", platform_services_dispatch_ui_thread_lifecycle); harness.run("platform services dispatch UI thread lifecycle", platform_services_dispatch_ui_thread_lifecycle);
harness.run("platform services dispatch render context lifecycle", platform_services_dispatch_render_context_lifecycle); harness.run("platform services dispatch render context lifecycle", platform_services_dispatch_render_context_lifecycle);
harness.run("platform services dispatch render capture hooks", platform_services_dispatch_render_capture_hooks);
harness.run("platform services dispatch frame hooks", platform_services_dispatch_frame_hooks); harness.run("platform services dispatch frame hooks", platform_services_dispatch_frame_hooks);
harness.run("platform services dispatch file actions", platform_services_dispatch_file_actions); harness.run("platform services dispatch file actions", platform_services_dispatch_file_actions);
harness.run("platform services dispatch picker callbacks", platform_services_dispatch_picker_callbacks); harness.run("platform services dispatch picker callbacks", platform_services_dispatch_picker_callbacks);