Split Apple platform target and move platform state ownership

This commit is contained in:
2026-06-17 01:03:01 +02:00
parent 5fdc9a9dd6
commit 90a55b86fe
18 changed files with 258 additions and 104 deletions

View File

@@ -239,8 +239,6 @@ add_library(pp_platform_api STATIC
src/platform_api/asset_file_load_policy.h src/platform_api/asset_file_load_policy.h
src/platform_api/network_tls_policy.cpp src/platform_api/network_tls_policy.cpp
src/platform_api/network_tls_policy.h src/platform_api/network_tls_policy.h
src/platform_apple/apple_platform_services.cpp
src/platform_apple/apple_platform_services.h
src/platform_api/platform_policy.cpp src/platform_api/platform_policy.cpp
src/platform_api/platform_policy.h src/platform_api/platform_policy.h
src/platform_api/platform_services.cpp src/platform_api/platform_services.cpp
@@ -255,6 +253,18 @@ target_link_libraries(pp_platform_api
PRIVATE PRIVATE
pp_project_warnings) pp_project_warnings)
add_library(pp_platform_apple STATIC
${PP_PLATFORM_APPLE_SOURCES})
target_include_directories(pp_platform_apple
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(pp_platform_apple
PUBLIC
pp_platform_api
pp_project_options
PRIVATE
pp_project_warnings)
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_dialog.h src/app_core/app_dialog.h
@@ -608,6 +618,9 @@ if(PP_BUILD_APP)
pp_project_options pp_project_options
PRIVATE PRIVATE
pp_project_warnings) pp_project_warnings)
if(APPLE)
target_link_libraries(panopainter_app PRIVATE pp_platform_apple)
endif()
pp_add_version_generation(panopainter_app "$<IF:$<CONFIG:Debug>,debug,release>") pp_add_version_generation(panopainter_app "$<IF:$<CONFIG:Debug>,debug,release>")
add_library(pp_platform_windows OBJECT add_library(pp_platform_windows OBJECT

View File

@@ -117,6 +117,11 @@ set(PP_PLATFORM_LINUX_SOURCES
src/platform_linux/linux_platform_services.h src/platform_linux/linux_platform_services.h
) )
set(PP_PLATFORM_APPLE_SOURCES
src/platform_apple/apple_platform_services.cpp
src/platform_apple/apple_platform_services.h
)
set(PP_PANOPAINTER_APP_SOURCES set(PP_PANOPAINTER_APP_SOURCES
src/app.cpp src/app.cpp
src/app_runtime.cpp src/app_runtime.cpp

View File

@@ -92,6 +92,20 @@ Current hotspot files:
Latest slice: Latest slice:
- `pp_platform_api` no longer compiles
`src/platform_apple/apple_platform_services.*`; Apple concrete platform
code now lives in the new `pp_platform_apple` target, and
`panopainter_app` plus `pp_platform_api_tests` link that concrete target
where needed.
- Retained GLFW window hooks/state and retained Apple UI/app handle snapshots
now live in `src/platform_legacy/legacy_platform_state.*` instead of staying
inline in `src/platform_legacy/legacy_platform_services.cpp`, which trims
another process-global platform-state pocket out of the legacy platform
shell and removes more direct `App::I` reads from touched platform paths.
- Windows VR session snapshot ownership now lives in
`src/platform_windows/windows_vr_shell.h` and
`src/platform_windows/windows_platform_services.*` instead of on `App`,
with app-side reads now routed through `App::vr_session_snapshot()`.
- The live Windows entry shell now routes through - The live Windows entry shell now routes through
`run_main_application(...)` in `run_main_application(...)` in
`src/platform_windows/windows_runtime_shell.*`, leaving `src/main.cpp` as a `src/platform_windows/windows_runtime_shell.*`, leaving `src/main.cpp` as a
@@ -172,8 +186,9 @@ Latest slice:
Current architecture mismatches that must be treated as real blockers: Current architecture mismatches that must be treated as real blockers:
- `pp_platform_api` still compiles Apple implementation files instead of only - `pp_platform_api` no longer compiles Apple implementation files, but it
platform-neutral policy and interface code. still owns concrete Linux platform sources instead of only platform-neutral
policy and interface code.
- `src/platform_apple/apple_platform_services.cpp` no longer reaches `App::I` - `src/platform_apple/apple_platform_services.cpp` no longer reaches `App::I`
directly, and Linux FPS title reporting now uses an injected callback, but directly, and Linux FPS title reporting now uses an injected callback, but
retained Apple bridging in `platform_legacy` and other platform/app coupling retained Apple bridging in `platform_legacy` and other platform/app coupling
@@ -185,8 +200,10 @@ Current architecture mismatches that must be treated as real blockers:
route through retained local GLFW callback hooks, and retained Apple ObjC route through retained local GLFW callback hooks, and retained Apple ObjC
handles plus storage paths now sit behind one local `platform_legacy` handles plus storage paths now sit behind one local `platform_legacy`
helper instead of being re-read through `App::I` in each touched path, with helper instead of being re-read through `App::I` in each touched path, with
the retained GLFW window hooks and fallback storage-path return now also the retained GLFW window hooks, Apple handle snapshots, and fallback
using local retained-state helpers instead of direct method-body reads. storage-path return now also using local retained-state helpers instead of
direct method-body reads, while Windows VR session snapshot state now also
lives behind platform-owned helpers instead of on `App`.
- `src/platform_legacy/legacy_platform_services.*` is still part of the live - `src/platform_legacy/legacy_platform_services.*` is still part of the live
app shell. app shell.
- `pp_panopainter_ui` still depends on `pp_legacy_app`. - `pp_panopainter_ui` still depends on `pp_legacy_app`.

View File

@@ -41,7 +41,8 @@ Completed, blocked, and superseded task history moved to
`src/node_stroke_preview.cpp`, `src/app.cpp`, `src/app_dialogs.cpp`, and the `src/node_stroke_preview.cpp`, `src/app.cpp`, `src/app_dialogs.cpp`, and the
extracted canvas/platform containment files. extracted canvas/platform containment files.
- The platform boundary is not finished: - The platform boundary is not finished:
- `pp_platform_api` still compiles Apple implementation files - `pp_platform_api` no longer compiles Apple implementation files, but it
still compiles concrete Linux platform sources
- `platform_apple` no longer reaches `App::I` directly, and Linux FPS title - `platform_apple` no longer reaches `App::I` directly, and Linux FPS title
reporting now uses an injected callback, but retained Apple bridging and reporting now uses an injected callback, but retained Apple bridging and
broader platform-to-app singleton reach are still open in broader platform-to-app singleton reach are still open in
@@ -1063,11 +1064,21 @@ layer.
#### ARC-PLT-001 - Split `pp_platform_api` From Concrete Platform Code #### ARC-PLT-001 - Split `pp_platform_api` From Concrete Platform Code
Status: Ready Status: In Progress
Why now: Why now:
`pp_platform_api` is supposed to be the SDK-free policy and interface layer, `pp_platform_api` is supposed to be the SDK-free policy and interface layer,
but it still compiles `src/platform_apple/apple_platform_services.*`. and while Apple implementation has now moved out, it still compiles concrete
Linux platform sources.
Current slice:
- `pp_platform_api` no longer compiles
`src/platform_apple/apple_platform_services.*`.
- Apple concrete platform code now lives in the new `pp_platform_apple`
target, and `panopainter_app` plus `pp_platform_api_tests` now link that
concrete target where needed.
- The dependency direction is cleaner for Apple, but the same split is still
incomplete for Linux and the broader concrete platform family.
Write scope: Write scope:
- `CMakeLists.txt` - `CMakeLists.txt`
@@ -1128,6 +1139,10 @@ Current slice:
- retained storage-path state now also lives in - retained storage-path state now also lives in
`src/platform_legacy/legacy_platform_state.*` instead of staying inline in `src/platform_legacy/legacy_platform_state.*` instead of staying inline in
`src/platform_legacy/legacy_platform_services.cpp` `src/platform_legacy/legacy_platform_services.cpp`
- retained GLFW window hooks/state and retained Apple UI/app handle state now
also live in `src/platform_legacy/legacy_platform_state.*`, and
`src/platform_legacy/legacy_platform_services.cpp` now consumes those
snapshots without direct `App::I` reads in the touched paths
- retained Apple callback injection and broader `platform_legacy` singleton - retained Apple callback injection and broader `platform_legacy` singleton
reach are still open reach are still open
- The remaining Win32 shell wrappers for close, async lock/swap, - The remaining Win32 shell wrappers for close, async lock/swap,
@@ -1160,12 +1175,22 @@ Mini-model packet:
#### ARC-PLT-003 - Remove App-Owned Cross-Platform Handle Storage #### ARC-PLT-003 - Remove App-Owned Cross-Platform Handle Storage
Status: Ready Status: In Progress
Why now: Why now:
`src/platform_legacy/legacy_platform_services.cpp` and `src/app.h` still keep `src/platform_legacy/legacy_platform_services.cpp` and `src/app.h` still keep
platform-handle state on `App`, which blocks a real `pp_platform_*` shell split. platform-handle state on `App`, which blocks a real `pp_platform_*` shell split.
Current slice:
- Windows VR session snapshot ownership no longer lives on `App`.
- `VrSessionSnapshot` now lives behind
`src/platform_windows/windows_vr_shell.h` and
`read_platform_vr_session_snapshot()` in
`src/platform_windows/windows_platform_services.*`, with app-side reads now
routed through `App::vr_session_snapshot()`.
- `App` still owns other platform-facing handles and retained legacy platform
state is not fully removed, so this remains a live ownership task.
Write scope: Write scope:
- `src/platform_legacy/legacy_platform_services.*` - `src/platform_legacy/legacy_platform_services.*`
- `src/app.h` - `src/app.h`

View File

@@ -77,6 +77,14 @@ struct VRController
virtual float get_trigger_value() const { return 1.f; } virtual float get_trigger_value() const { return 1.f; }
}; };
struct VrSessionSnapshot
{
bool has_vr = false;
bool vr_active = false;
std::array<VRController, 2> vr_controllers{};
glm::mat4 vr_head{1.0f};
};
class App class App
{ {
public: public:
@@ -125,7 +133,6 @@ public:
std::string doc_dir; std::string doc_dir;
std::string doc_filename; std::string doc_filename;
bool has_stylus = false; bool has_stylus = false;
bool has_vr = false;
bool vr_controllers_enabled = true; bool vr_controllers_enabled = true;
float off_x = 0; float off_x = 0;
float off_y = 0; float off_y = 0;
@@ -136,10 +143,7 @@ public:
bool animate = false; bool animate = false;
bool ui_visible = true; bool ui_visible = true;
bool ui_rtl = false; bool ui_rtl = false;
bool vr_active = false;
bool vr_only = false; bool vr_only = false;
VRController vr_controllers[2];
glm::mat4 vr_head;
float vr_pressure = 1.f; float vr_pressure = 1.f;
glm::mat4 vr_rot{ 0 }; glm::mat4 vr_rot{ 0 };
glm::mat4 vr_uirot{ 0 }; glm::mat4 vr_uirot{ 0 };
@@ -209,6 +213,7 @@ public:
[[nodiscard]] bool platform_enables_live_asset_reloading(); [[nodiscard]] bool platform_enables_live_asset_reloading();
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);
[[nodiscard]] VrSessionSnapshot vr_session_snapshot() const;
void save_prepared_file( void save_prepared_file(
std::string path, std::string path,
std::string suggested_name, std::string suggested_name,

View File

@@ -12,6 +12,9 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "platform_linux/linux_platform_services.h" #include "platform_linux/linux_platform_services.h"
#endif #endif
#ifdef _WIN32
#include "platform_windows/windows_platform_services.h"
#endif
#include "platform_legacy/legacy_platform_services.h" #include "platform_legacy/legacy_platform_services.h"
#include "renderer_gl/opengl_capabilities.h" #include "renderer_gl/opengl_capabilities.h"
@@ -435,6 +438,15 @@ void App::report_rendered_frames(int frames)
active_platform_services().report_rendered_frames(frames); active_platform_services().report_rendered_frames(frames);
} }
VrSessionSnapshot App::vr_session_snapshot() const
{
#ifdef _WIN32
return pp::platform::windows::read_platform_vr_session_snapshot();
#else
return {};
#endif
}
void App::save_prepared_file( void App::save_prepared_file(
std::string path, std::string path,
std::string suggested_name, std::string suggested_name,
@@ -655,10 +667,11 @@ bool App::touch_tap(const glm::vec2& pos, int fingers, int tap_count)
} }
bool App::key_down(kKey key) bool App::key_down(kKey key)
{ {
const auto vr_session = vr_session_snapshot();
const auto plan = pp::app::plan_app_key_down_dispatch( const auto plan = pp::app::plan_app_key_down_dispatch(
layout.get(main_id) != nullptr, layout.get(main_id) != nullptr,
key == kKey::KeySpacebar, key == kKey::KeySpacebar,
vr_active); vr_session.vr_active);
if (plan.sync_vr_camera_rotation) if (plan.sync_vr_camera_rotation)
canvas->m_canvas->m_cam_rot = vr_rot; canvas->m_canvas->m_cam_rot = vr_rot;
redraw = plan.request_redraw; redraw = plan.request_redraw;

View File

@@ -357,7 +357,7 @@ void bind_legacy_tools_menu(App& app)
if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr")) if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr"))
{ {
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check"); NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
cb->set_value(app.has_vr); cb->set_value(app.vr_session_snapshot().has_vr);
vr_btn->on_click = [vr_btn](Node*) vr_btn->on_click = [vr_btn](Node*)
{ {

View File

@@ -128,6 +128,8 @@ void App::vr_update(float dt)
{ {
if (!vr_controllers_enabled) if (!vr_controllers_enabled)
return; return;
const auto vr_session = vr_session_snapshot();
canvas->m_canvas->m_cam_fov = 60; canvas->m_canvas->m_cam_fov = 60;
float tan_fov = glm::tan(glm::radians(canvas->m_canvas->m_cam_fov / 2.f)); float tan_fov = glm::tan(glm::radians(canvas->m_canvas->m_cam_fov / 2.f));
glm::vec3 aspect = { (float)uirtt.getWidth() / (float)uirtt.getHeight(), 1.f, 1.f }; glm::vec3 aspect = { (float)uirtt.getWidth() / (float)uirtt.getHeight(), 1.f, 1.f };
@@ -142,8 +144,8 @@ void App::vr_update(float dt)
auto r = glm::normalize(glm::vec3(glm::vec4(1, 0, 0, 0) * mm)); auto r = glm::normalize(glm::vec3(glm::vec4(1, 0, 0, 0) * mm));
auto n = glm::normalize(glm::vec3(glm::vec4(0, 0, 1, 0) * mm)); auto n = glm::normalize(glm::vec3(glm::vec4(0, 0, 1, 0) * mm));
auto u = glm::normalize(glm::vec3(glm::vec4(0, 1, 0, 0) * mm)); auto u = glm::normalize(glm::vec3(glm::vec4(0, 1, 0, 0) * mm));
auto co = vr_controllers[0].get_pos(); auto co = vr_session.vr_controllers[0].get_pos();
auto cd = glm::mat3(vr_controllers[0].m_mat) * glm::mat3(glm::eulerAngleX(glm::radians(-30.f))) * glm::vec3(0, 0, -1); auto cd = glm::mat3(vr_session.vr_controllers[0].m_mat) * glm::mat3(glm::eulerAngleX(glm::radians(-30.f))) * glm::vec3(0, 0, -1);
ui_inside = false; ui_inside = false;
glm::vec3 hit; glm::vec3 hit;
float t; float t;
@@ -167,7 +169,7 @@ void App::vr_update(float dt)
if (down_controller) if (down_controller)
{ {
glm::vec3 head_position = vr_head[3]; glm::vec3 head_position = vr_session.vr_head[3];
glm::vec3 c_pos = glm::normalize(down_controller->get_pos() - head_position) * 800.f; glm::vec3 c_pos = glm::normalize(down_controller->get_pos() - head_position) * 800.f;
controller_points.add(c_pos); controller_points.add(c_pos);
auto p = controller_points.average(); auto p = controller_points.average();
@@ -210,7 +212,7 @@ void App::vr_analog(const VRController& c, VRController::kButton b, VRController
{ {
if (a == VRController::kAction::Press) if (a == VRController::kAction::Press)
{ {
glm::vec3 head_position = vr_head[3]; glm::vec3 head_position = vr_session_snapshot().vr_head[3];
glm::vec3 c_pos = glm::normalize(c.get_pos() - head_position) * 800.f; glm::vec3 c_pos = glm::normalize(c.get_pos() - head_position) * 800.f;
render_task_async([=] { render_task_async([=] {
Canvas::I->stroke_start(c_pos, force.x); Canvas::I->stroke_start(c_pos, force.x);
@@ -517,7 +519,8 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat
// draw the motion controller sphere // draw the motion controller sphere
if (vr_controllers_enabled && ui_visible && ui_inside) if (vr_controllers_enabled && ui_visible && ui_inside)
{ {
auto mvp = proj * camera * vr_controllers[0].m_mat * glm::eulerAngleX(glm::radians(-30.f)); const auto vr_session = vr_session_snapshot();
auto mvp = proj * camera * vr_session.vr_controllers[0].m_mat * glm::eulerAngleX(glm::radians(-30.f));
pp::panopainter::setup_legacy_vr_color_shader( pp::panopainter::setup_legacy_vr_color_shader(
pp::panopainter::LegacyVrColorUniforms { pp::panopainter::LegacyVrColorUniforms {
.color = { 1, 0, 1, 1 }, .color = { 1, 0, 1, 1 },
@@ -530,7 +533,8 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat
// draw the motion controller brush // draw the motion controller brush
if (vr_controllers_enabled && (!ui_visible || !ui_inside)) if (vr_controllers_enabled && (!ui_visible || !ui_inside))
{ {
glm::vec3 cpos = vr_controllers[0].get_pos() - xyz(pose[3]); const auto vr_session = vr_session_snapshot();
glm::vec3 cpos = vr_session.vr_controllers[0].get_pos() - xyz(pose[3]);
auto pos = glm::translate(glm::normalize(cpos) * 100.f); auto pos = glm::translate(glm::normalize(cpos) * 100.f);
auto tip_color = glm::vec4(glm::vec3(canvas->m_canvas->m_current_brush->m_tip_color), 1); auto tip_color = glm::vec4(glm::vec3(canvas->m_canvas->m_current_brush->m_tip_color), 1);
pp::panopainter::setup_legacy_vr_stroke_preview_shader( pp::panopainter::setup_legacy_vr_stroke_preview_shader(

View File

@@ -110,10 +110,11 @@ void execute_legacy_app_ui_thread_stop(App& app)
void App::draw(float dt) void App::draw(float dt)
{ {
const auto vr_session = vr_session_snapshot();
const auto draw_plan = pp::app::plan_app_frame_draw( const auto draw_plan = pp::app::plan_app_frame_draw(
canvas != nullptr, canvas != nullptr,
canvas && canvas->m_canvas, canvas && canvas->m_canvas,
vr_active, vr_session.vr_active,
ui_visible, ui_visible,
vr_only); vr_only);

View File

@@ -106,7 +106,8 @@ void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
} }
else else
{ {
auto dir = (App::I->has_vr && App::I->vr_active) ? glm::vec2(1, 1) : glm::vec2(-1, -1); const auto vr_session = App::I->vr_session_snapshot();
auto dir = (vr_session.has_vr && vr_session.vr_active) ? glm::vec2(1, 1) : glm::vec2(-1, -1);
Canvas::I->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * dir * (Canvas::I->m_cam_fov / 85.f); Canvas::I->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * dir * (Canvas::I->m_cam_fov / 85.f);
auto angle = Canvas::I->m_pan * 0.003f; auto angle = Canvas::I->m_pan * 0.003f;
Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x); Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x);

View File

@@ -2,7 +2,6 @@
#include "platform_legacy/legacy_platform_services.h" #include "platform_legacy/legacy_platform_services.h"
#include "platform_legacy/legacy_platform_state.h" #include "platform_legacy/legacy_platform_state.h"
#include "app.h"
#include "legacy_ui_gl_dispatch.h" #include "legacy_ui_gl_dispatch.h"
#include "log.h" #include "log.h"
#include "platform_apple/apple_platform_services.h" #include "platform_apple/apple_platform_services.h"
@@ -36,13 +35,11 @@ void delete_all_files_in_path(const std::string& source_path);
void save_image_library(const std::string& path); void save_image_library(const std::string& path);
#endif #endif
#elif __LINUX__ #elif __LINUX__
#include <GLFW/glfw3.h>
#include <tinyfiledialogs.h> #include <tinyfiledialogs.h>
#include "platform_linux/linux_platform_services.h" #include "platform_linux/linux_platform_services.h"
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__
#include <GLFW/glfw3.h>
void webgl_pick_file(std::function<void(std::string)> callback); void webgl_pick_file(std::function<void(std::string)> callback);
void webgl_pick_file_save( void webgl_pick_file_save(
const std::string& path, const std::string& path,
@@ -95,43 +92,6 @@ public:
#endif #endif
} }
#if defined(__LINUX__) || defined(__WEB__)
struct RetainedLegacyGlfwWindowHooks final {
std::function<void()> acquire_render_context;
std::function<void()> present_render_context;
std::function<void()> request_app_close;
};
struct RetainedLegacyGlfwWindowState final {
decltype(App::I->glfw_window) window = nullptr;
RetainedLegacyGlfwWindowHooks hooks;
};
[[nodiscard]] RetainedLegacyGlfwWindowState& active_legacy_glfw_window_state()
{
static RetainedLegacyGlfwWindowState state = [] {
RetainedLegacyGlfwWindowState retained;
retained.window = App::I->glfw_window;
retained.hooks.acquire_render_context = [window = retained.window] {
glfwMakeContextCurrent(window);
};
retained.hooks.present_render_context = [window = retained.window] {
glfwSwapBuffers(window);
};
retained.hooks.request_app_close = [window = retained.window] {
glfwSetWindowShouldClose(window, GLFW_TRUE);
};
return retained;
}();
return state;
}
[[nodiscard]] RetainedLegacyGlfwWindowHooks& active_legacy_glfw_window_hooks()
{
return active_legacy_glfw_window_state().hooks;
}
#endif
#if defined(__IOS__) || defined(__OSX__) #if defined(__IOS__) || defined(__OSX__)
[[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types) [[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types)
{ {
@@ -143,47 +103,21 @@ struct RetainedLegacyGlfwWindowState final {
return types; return types;
} }
struct RetainedLegacyAppleState final {
#ifdef __IOS__
decltype(App::I->ios_view) ios_view = nullptr;
decltype(App::I->ios_app) ios_app = nullptr;
#elif defined(__OSX__)
decltype(App::I->osx_view) osx_view = nullptr;
decltype(App::I->osx_app) osx_app = nullptr;
#endif
};
[[nodiscard]] RetainedLegacyAppleState& active_legacy_apple_state()
{
static RetainedLegacyAppleState state = [] {
RetainedLegacyAppleState retained;
#ifdef __IOS__
retained.ios_view = App::I->ios_view;
retained.ios_app = App::I->ios_app;
#elif defined(__OSX__)
retained.osx_view = App::I->osx_view;
retained.osx_app = App::I->osx_app;
#endif
return retained;
}();
return state;
}
[[nodiscard]] pp::platform::PlatformStoragePaths prepare_legacy_apple_storage_paths() [[nodiscard]] pp::platform::PlatformStoragePaths prepare_legacy_apple_storage_paths()
{ {
const auto& apple_state = active_legacy_apple_state(); const auto& apple_state = pp::platform::legacy::active_legacy_apple_state();
#ifdef __IOS__ #ifdef __IOS__
[apple_state.ios_view init_dirs]; [apple_state.ios_view init_dirs];
#elif defined(__OSX__) #elif defined(__OSX__)
[apple_state.osx_app init_dirs]; [apple_state.osx_app init_dirs];
#endif #endif
return active_legacy_storage_paths(); return pp::platform::legacy::active_legacy_storage_paths();
} }
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services() [[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
{ {
#ifdef __IOS__ #ifdef __IOS__
const auto& apple_state = active_legacy_apple_state(); const auto& apple_state = pp::platform::legacy::active_legacy_apple_state();
auto* const ios_view = apple_state.ios_view; auto* const ios_view = apple_state.ios_view;
auto* const ios_app = apple_state.ios_app; auto* const ios_app = apple_state.ios_app;
static pp::platform::apple::AppleDocumentPlatformServices services( static pp::platform::apple::AppleDocumentPlatformServices services(
@@ -259,7 +193,7 @@ struct RetainedLegacyAppleState final {
}()); }());
return services; return services;
#else #else
const auto& apple_state = active_legacy_apple_state(); const auto& apple_state = pp::platform::legacy::active_legacy_apple_state();
auto* const osx_view = apple_state.osx_view; auto* const osx_view = apple_state.osx_view;
auto* const osx_app = apple_state.osx_app; auto* const osx_app = apple_state.osx_app;
static pp::platform::apple::AppleDocumentPlatformServices services( static pp::platform::apple::AppleDocumentPlatformServices services(
@@ -470,7 +404,7 @@ public:
#elif __ANDROID__ #elif __ANDROID__
android_async_lock(); android_async_lock();
#elif __LINUX__ || __WEB__ #elif __LINUX__ || __WEB__
active_legacy_glfw_window_hooks().acquire_render_context(); pp::platform::legacy::active_legacy_glfw_window_hooks().acquire_render_context();
#endif #endif
} }
@@ -490,7 +424,7 @@ public:
#elif __ANDROID__ #elif __ANDROID__
android_async_swap(); android_async_swap();
#elif __LINUX__ || __WEB__ #elif __LINUX__ || __WEB__
active_legacy_glfw_window_hooks().present_render_context(); pp::platform::legacy::active_legacy_glfw_window_hooks().present_render_context();
#endif #endif
} }
@@ -801,7 +735,7 @@ public:
#ifdef __OSX__ #ifdef __OSX__
active_apple_document_platform_services().request_app_close(); active_apple_document_platform_services().request_app_close();
#elif __LINUX__ #elif __LINUX__
active_legacy_glfw_window_hooks().request_app_close(); pp::platform::legacy::active_legacy_glfw_window_hooks().request_app_close();
#endif #endif
} }

View File

@@ -3,6 +3,10 @@
#include "app.h" #include "app.h"
#if defined(__LINUX__) || defined(__WEB__)
#include <GLFW/glfw3.h>
#endif
namespace pp::platform::legacy { namespace pp::platform::legacy {
namespace { namespace {
@@ -12,6 +16,50 @@ struct RetainedLegacyStoragePaths final {
} }
#if defined(__LINUX__) || defined(__WEB__)
[[nodiscard]] RetainedLegacyGlfwWindowState& active_legacy_glfw_window_state()
{
static RetainedLegacyGlfwWindowState state = [] {
RetainedLegacyGlfwWindowState retained;
retained.window = App::I->glfw_window;
retained.hooks.acquire_render_context = [window = retained.window] {
glfwMakeContextCurrent(window);
};
retained.hooks.present_render_context = [window = retained.window] {
glfwSwapBuffers(window);
};
retained.hooks.request_app_close = [window = retained.window] {
glfwSetWindowShouldClose(window, GLFW_TRUE);
};
return retained;
}();
return state;
}
[[nodiscard]] RetainedLegacyGlfwWindowHooks& active_legacy_glfw_window_hooks()
{
return active_legacy_glfw_window_state().hooks;
}
#endif
#if defined(__IOS__) || defined(__OSX__)
[[nodiscard]] RetainedLegacyAppleState& active_legacy_apple_state()
{
static RetainedLegacyAppleState state = [] {
RetainedLegacyAppleState retained;
#ifdef __IOS__
retained.ios_view = App::I->ios_view;
retained.ios_app = App::I->ios_app;
#elif defined(__OSX__)
retained.osx_view = App::I->osx_view;
retained.osx_app = App::I->osx_app;
#endif
return retained;
}();
return state;
}
#endif
[[nodiscard]] const pp::platform::PlatformStoragePaths& active_legacy_storage_paths() [[nodiscard]] const pp::platform::PlatformStoragePaths& active_legacy_storage_paths()
{ {
static RetainedLegacyStoragePaths state = [] { static RetainedLegacyStoragePaths state = [] {

View File

@@ -2,8 +2,52 @@
#include "platform_api/platform_services.h" #include "platform_api/platform_services.h"
#if __LINUX__ || __WEB__
struct GLFWwindow;
#endif
#if defined(__OBJC__) && defined(__IOS__)
@class GameViewController;
@class AppDelegate;
#endif
#if defined(__OBJC__) && defined(__OSX__)
@class View;
@class AppOSX;
#endif
namespace pp::platform::legacy { namespace pp::platform::legacy {
#if defined(__LINUX__) || defined(__WEB__)
struct RetainedLegacyGlfwWindowHooks final {
std::function<void()> acquire_render_context;
std::function<void()> present_render_context;
std::function<void()> request_app_close;
};
struct RetainedLegacyGlfwWindowState final {
GLFWwindow* window = nullptr;
RetainedLegacyGlfwWindowHooks hooks;
};
[[nodiscard]] RetainedLegacyGlfwWindowState& active_legacy_glfw_window_state();
[[nodiscard]] RetainedLegacyGlfwWindowHooks& active_legacy_glfw_window_hooks();
#endif
#if defined(__IOS__) || defined(__OSX__)
struct RetainedLegacyAppleState final {
#ifdef __IOS__
GameViewController* ios_view = nullptr;
AppDelegate* ios_app = nullptr;
#elif defined(__OSX__)
View* osx_view = nullptr;
AppOSX* osx_app = nullptr;
#endif
};
[[nodiscard]] RetainedLegacyAppleState& active_legacy_apple_state();
#endif
[[nodiscard]] const pp::platform::PlatformStoragePaths& active_legacy_storage_paths(); [[nodiscard]] const pp::platform::PlatformStoragePaths& active_legacy_storage_paths();
} }

View File

@@ -63,7 +63,7 @@ void update_window_fps(HWND hWnd, const wchar_t* window_title, VrShellState& vr,
{ {
static wchar_t title_fps[512]; static wchar_t title_fps[512];
const int vr_fps = current_vr_fps(vr); const int vr_fps = current_vr_fps(vr);
if (App::I->vr_active) if (read_vr_session_snapshot(vr).vr_active)
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", window_title, frames, vr_fps); swprintf_s(title_fps, L"%s - %d fps - %d vr fps", window_title, frames, vr_fps);
else else
swprintf_s(title_fps, L"%s - %d fps", window_title, frames); swprintf_s(title_fps, L"%s - %d fps", window_title, frames);

View File

@@ -793,4 +793,9 @@ PlatformServices& platform_services()
return services; return services;
} }
VrSessionSnapshot read_platform_vr_session_snapshot() noexcept
{
return read_vr_session_snapshot(retained_state().vr);
}
} }

View File

@@ -4,9 +4,12 @@
#include <future> #include <future>
struct VrSessionSnapshot;
namespace pp::platform::windows { namespace pp::platform::windows {
[[nodiscard]] PlatformServices& platform_services(); [[nodiscard]] PlatformServices& platform_services();
[[nodiscard]] VrSessionSnapshot read_platform_vr_session_snapshot() noexcept;
void enqueue_main_thread_task(std::packaged_task<void()> task); void enqueue_main_thread_task(std::packaged_task<void()> task);
void drain_main_thread_tasks(); void drain_main_thread_tasks();

View File

@@ -14,6 +14,8 @@
namespace pp::platform::windows { namespace pp::platform::windows {
struct VrShellState final { struct VrShellState final {
std::mutex snapshot_mutex;
VrSessionSnapshot session;
std::jthread hmd_renderer; std::jthread hmd_renderer;
std::mutex hmd_render_mutex; std::mutex hmd_render_mutex;
std::condition_variable hmd_render_cv; std::condition_variable hmd_render_cv;
@@ -27,6 +29,35 @@ inline int current_vr_fps(const VrShellState& state)
return state.vr_frames.load(std::memory_order_relaxed); return state.vr_frames.load(std::memory_order_relaxed);
} }
inline VrSessionSnapshot read_vr_session_snapshot(VrShellState& state)
{
std::lock_guard<std::mutex> lock(state.snapshot_mutex);
return state.session;
}
inline void write_vr_session_snapshot(
VrShellState& state,
bool has_vr,
bool vr_active,
const VRController& primary_controller,
const glm::mat4& vr_head)
{
std::lock_guard<std::mutex> lock(state.snapshot_mutex);
state.session.has_vr = has_vr;
state.session.vr_active = vr_active;
state.session.vr_controllers[0] = primary_controller;
state.session.vr_head = vr_head;
}
inline void clear_vr_session_snapshot(VrShellState& state, bool has_vr)
{
std::lock_guard<std::mutex> lock(state.snapshot_mutex);
state.session.has_vr = has_vr;
state.session.vr_active = false;
state.session.vr_controllers[0] = {};
state.session.vr_head = glm::mat4(1.0f);
}
inline void request_stop_and_join_vr_thread(VrShellState& state) inline void request_stop_and_join_vr_thread(VrShellState& state)
{ {
if (state.hmd_renderer.joinable()) if (state.hmd_renderer.joinable())
@@ -64,7 +95,7 @@ inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>
BT_SetTerminate(); BT_SetTerminate();
LOG("start hmd render thread"); LOG("start hmd render thread");
App::I->has_vr = true; clear_vr_session_snapshot(state, true);
state.vr_running.store(true, std::memory_order_relaxed); state.vr_running.store(true, std::memory_order_relaxed);
state.vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1, state.vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1,
@@ -99,9 +130,12 @@ inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>
frames++; frames++;
state.vive->Update(); state.vive->Update();
App::I->vr_active = state.vive->m_active; write_vr_session_snapshot(
App::I->vr_controllers[0] = state.vive->m_controllers[0]; state,
App::I->vr_head = state.vive->m_pose; true,
state.vive->m_active,
state.vive->m_controllers[0],
state.vive->m_pose);
App::I->vr_update(dt); App::I->vr_update(dt);
if (state.vr_running.load(std::memory_order_relaxed) && state.vive->m_active) if (state.vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
@@ -118,8 +152,7 @@ inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>
t0 = t1; t0 = t1;
} }
App::I->vr_active = false; clear_vr_session_snapshot(state, false);
App::I->has_vr = false;
state.vr_running.store(false, std::memory_order_relaxed); state.vr_running.store(false, std::memory_order_relaxed);
state.vive->Terminate(); state.vive->Terminate();
LOG("hmd renderer terminated"); LOG("hmd renderer terminated");

View File

@@ -352,6 +352,9 @@ add_executable(pp_platform_api_tests
target_link_libraries(pp_platform_api_tests PRIVATE target_link_libraries(pp_platform_api_tests PRIVATE
pp_platform_api pp_platform_api
pp_test_harness) pp_test_harness)
if(TARGET pp_platform_apple)
target_link_libraries(pp_platform_api_tests PRIVATE pp_platform_apple)
endif()
add_test(NAME pp_platform_api_tests COMMAND pp_platform_api_tests) add_test(NAME pp_platform_api_tests COMMAND pp_platform_api_tests)
set_tests_properties(pp_platform_api_tests PROPERTIES set_tests_properties(pp_platform_api_tests PROPERTIES