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

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

View File

@@ -12,6 +12,9 @@
#include <GLFW/glfw3.h>
#include "platform_linux/linux_platform_services.h"
#endif
#ifdef _WIN32
#include "platform_windows/windows_platform_services.h"
#endif
#include "platform_legacy/legacy_platform_services.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);
}
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(
std::string path,
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)
{
const auto vr_session = vr_session_snapshot();
const auto plan = pp::app::plan_app_key_down_dispatch(
layout.get(main_id) != nullptr,
key == kKey::KeySpacebar,
vr_active);
vr_session.vr_active);
if (plan.sync_vr_camera_rotation)
canvas->m_canvas->m_cam_rot = vr_rot;
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"))
{
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*)
{

View File

@@ -128,6 +128,8 @@ void App::vr_update(float dt)
{
if (!vr_controllers_enabled)
return;
const auto vr_session = vr_session_snapshot();
canvas->m_canvas->m_cam_fov = 60;
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 };
@@ -142,8 +144,8 @@ void App::vr_update(float dt)
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 u = glm::normalize(glm::vec3(glm::vec4(0, 1, 0, 0) * mm));
auto co = 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 co = vr_session.vr_controllers[0].get_pos();
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;
glm::vec3 hit;
float t;
@@ -167,7 +169,7 @@ void App::vr_update(float dt)
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;
controller_points.add(c_pos);
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)
{
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;
render_task_async([=] {
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
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::LegacyVrColorUniforms {
.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
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 tip_color = glm::vec4(glm::vec3(canvas->m_canvas->m_current_brush->m_tip_color), 1);
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)
{
const auto vr_session = vr_session_snapshot();
const auto draw_plan = pp::app::plan_app_frame_draw(
canvas != nullptr,
canvas && canvas->m_canvas,
vr_active,
vr_session.vr_active,
ui_visible,
vr_only);

View File

@@ -106,7 +106,8 @@ void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
}
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);
auto angle = Canvas::I->m_pan * 0.003f;
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_state.h"
#include "app.h"
#include "legacy_ui_gl_dispatch.h"
#include "log.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);
#endif
#elif __LINUX__
#include <GLFW/glfw3.h>
#include <tinyfiledialogs.h>
#include "platform_linux/linux_platform_services.h"
std::string linux_home_path();
int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE);
#elif __WEB__
#include <GLFW/glfw3.h>
void webgl_pick_file(std::function<void(std::string)> callback);
void webgl_pick_file_save(
const std::string& path,
@@ -95,43 +92,6 @@ public:
#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__)
[[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types)
{
@@ -143,47 +103,21 @@ struct RetainedLegacyGlfwWindowState final {
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()
{
const auto& apple_state = active_legacy_apple_state();
const auto& apple_state = pp::platform::legacy::active_legacy_apple_state();
#ifdef __IOS__
[apple_state.ios_view init_dirs];
#elif defined(__OSX__)
[apple_state.osx_app init_dirs];
#endif
return active_legacy_storage_paths();
return pp::platform::legacy::active_legacy_storage_paths();
}
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
{
#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_app = apple_state.ios_app;
static pp::platform::apple::AppleDocumentPlatformServices services(
@@ -259,7 +193,7 @@ struct RetainedLegacyAppleState final {
}());
return services;
#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_app = apple_state.osx_app;
static pp::platform::apple::AppleDocumentPlatformServices services(
@@ -470,7 +404,7 @@ public:
#elif __ANDROID__
android_async_lock();
#elif __LINUX__ || __WEB__
active_legacy_glfw_window_hooks().acquire_render_context();
pp::platform::legacy::active_legacy_glfw_window_hooks().acquire_render_context();
#endif
}
@@ -490,7 +424,7 @@ public:
#elif __ANDROID__
android_async_swap();
#elif __LINUX__ || __WEB__
active_legacy_glfw_window_hooks().present_render_context();
pp::platform::legacy::active_legacy_glfw_window_hooks().present_render_context();
#endif
}
@@ -801,7 +735,7 @@ public:
#ifdef __OSX__
active_apple_document_platform_services().request_app_close();
#elif __LINUX__
active_legacy_glfw_window_hooks().request_app_close();
pp::platform::legacy::active_legacy_glfw_window_hooks().request_app_close();
#endif
}

View File

@@ -3,6 +3,10 @@
#include "app.h"
#if defined(__LINUX__) || defined(__WEB__)
#include <GLFW/glfw3.h>
#endif
namespace pp::platform::legacy {
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()
{
static RetainedLegacyStoragePaths state = [] {

View File

@@ -2,8 +2,52 @@
#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 {
#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();
}

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];
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);
else
swprintf_s(title_fps, L"%s - %d fps", window_title, frames);

View File

@@ -793,4 +793,9 @@ PlatformServices& platform_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>
struct VrSessionSnapshot;
namespace pp::platform::windows {
[[nodiscard]] PlatformServices& platform_services();
[[nodiscard]] VrSessionSnapshot read_platform_vr_session_snapshot() noexcept;
void enqueue_main_thread_task(std::packaged_task<void()> task);
void drain_main_thread_tasks();

View File

@@ -14,6 +14,8 @@
namespace pp::platform::windows {
struct VrShellState final {
std::mutex snapshot_mutex;
VrSessionSnapshot session;
std::jthread hmd_renderer;
std::mutex hmd_render_mutex;
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);
}
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)
{
if (state.hmd_renderer.joinable())
@@ -64,7 +95,7 @@ inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>
BT_SetTerminate();
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.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++;
state.vive->Update();
App::I->vr_active = state.vive->m_active;
App::I->vr_controllers[0] = state.vive->m_controllers[0];
App::I->vr_head = state.vive->m_pose;
write_vr_session_snapshot(
state,
true,
state.vive->m_active,
state.vive->m_controllers[0],
state.vive->m_pose);
App::I->vr_update(dt);
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;
}
App::I->vr_active = false;
App::I->has_vr = false;
clear_vr_session_snapshot(state, false);
state.vr_running.store(false, std::memory_order_relaxed);
state.vive->Terminate();
LOG("hmd renderer terminated");