Extract dialog, VR, and canvas draw helpers
This commit is contained in:
@@ -92,6 +92,7 @@ set(PP_PANOPAINTER_APP_SOURCES
|
||||
src/app_cloud.cpp
|
||||
src/app_commands.cpp
|
||||
src/app_dialogs.cpp
|
||||
src/app_dialogs_info_openers.cpp
|
||||
src/app_events.cpp
|
||||
src/app_layout.cpp
|
||||
src/app_layout_about_layer_menu.cpp
|
||||
|
||||
@@ -83,12 +83,12 @@ Current hotspot files:
|
||||
- `src/app_layout.cpp`: 1249 lines
|
||||
- `src/canvas_modes.cpp`: 1798 lines
|
||||
- `src/node.cpp`: 1551 lines
|
||||
- `src/main.cpp`: 1218 lines
|
||||
- `src/main.cpp`: 1117 lines
|
||||
- `src/node_panel_brush.cpp`: 1197 lines
|
||||
- `src/node_stroke_preview.cpp`: 933 lines
|
||||
- `src/node_canvas.cpp`: 968 lines
|
||||
- `src/node_canvas.cpp`: 953 lines
|
||||
- `src/app.cpp`: 950 lines
|
||||
- `src/app_dialogs.cpp`: 908 lines
|
||||
- `src/app_dialogs.cpp`: 789 lines
|
||||
|
||||
Current architecture mismatches that must be treated as real blockers:
|
||||
|
||||
@@ -124,8 +124,11 @@ Current architecture mismatches that must be treated as real blockers:
|
||||
per-layer render-path orchestration now also route through retained
|
||||
draw-merge helpers even though the broader node draw loop is still inline,
|
||||
with the non-`draw_merged` outer layer/plane traversal now also routing
|
||||
through `execute_legacy_canvas_draw_layer_traversal(...)` while the heavier
|
||||
per-layer GL setup and draw lambdas still live in `src/node_canvas.cpp`.
|
||||
through `execute_legacy_canvas_draw_layer_traversal(...)`, while the heavier
|
||||
per-layer GL setup now also routes through
|
||||
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)` even though the
|
||||
remaining draw lambdas and broader node draw loop still live in
|
||||
`src/node_canvas.cpp`.
|
||||
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
||||
rather than thin composition/binding surfaces, even though tools-menu binding
|
||||
plus nested panels/options submenu wiring now live in
|
||||
@@ -134,7 +137,9 @@ Current architecture mismatches that must be treated as real blockers:
|
||||
live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a
|
||||
thin call-through, while about-menu and layer-menu wiring now also live in
|
||||
`src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus
|
||||
`App::init_menu_layer()` are now thin call-throughs.
|
||||
`App::init_menu_layer()` are now thin call-throughs, while the informational
|
||||
overlay opener family now also lives in `src/app_dialogs_info_openers.cpp`
|
||||
and the corresponding `App::dialog_*` entrypoints are thinner.
|
||||
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
||||
global singleton reach, raw observer pointers, retained static worker
|
||||
ownership in several app families, and ad hoc mutex/condition-variable
|
||||
@@ -161,6 +166,9 @@ Current architecture mismatches that must be treated as real blockers:
|
||||
`src/main.cpp`, while Win32 pointer API loading, stylus/ink timer decay,
|
||||
Wintab packet reset, and `WM_POINTERUPDATE` pen/touch handling now also live
|
||||
in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
||||
while the retained Win32 VR/HMD shell now also routes through
|
||||
`src/platform_windows/windows_vr_shell.h` instead of staying inline in
|
||||
`src/main.cpp`,
|
||||
while `App::rec_loop()` now delegates worker-iteration orchestration into
|
||||
the retained recording bridge, `App::update_rec_frames()` now delegates
|
||||
recording label refresh through that same retained recording path, and the
|
||||
|
||||
@@ -195,8 +195,11 @@ Current slice:
|
||||
broader draw-loop and renderer-state shell sequencing.
|
||||
- `NodeCanvas` outer non-`draw_merged` layer/plane traversal, onion-range
|
||||
failure handling, and visit payload setup now also route through
|
||||
`execute_legacy_canvas_draw_layer_traversal(...)`, but the node still owns
|
||||
the heavier per-layer GL setup and draw lambdas.
|
||||
`execute_legacy_canvas_draw_layer_traversal(...)`.
|
||||
- `NodeCanvas` non-`draw_merged` per-layer temporary erase/paint, layer-texture,
|
||||
and blend setup now also route through
|
||||
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)`, but the node
|
||||
still owns the remaining draw lambdas and broader renderer-state shell.
|
||||
|
||||
Write scope:
|
||||
- `src/node_stroke_preview.cpp`
|
||||
@@ -325,6 +328,13 @@ Why now:
|
||||
`src/app_dialogs.cpp` still mixes document workflow decisions, export routing,
|
||||
dialog construction, and overlay ownership.
|
||||
|
||||
Current slice:
|
||||
- Informational overlay opener paths for user manual, changelog, about,
|
||||
what's-new, and shortcuts now live in `src/app_dialogs_info_openers.cpp`,
|
||||
and the corresponding `App::dialog_*` entrypoints are now thin call-throughs,
|
||||
but document/export workflow and retained dialog execution are still inline in
|
||||
`src/app_dialogs.cpp`.
|
||||
|
||||
Write scope:
|
||||
- `src/app_dialogs.cpp`
|
||||
- `src/legacy_app_dialog_services.*`
|
||||
@@ -417,6 +427,10 @@ Current slice:
|
||||
reset, and `WM_POINTERUPDATE` pen/touch handling now live in
|
||||
`src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
||||
but broader retained Win32 shell state is still open
|
||||
- the retained Win32 VR/HMD shell, including worker start/stop and VR FPS
|
||||
state, now routes through `src/platform_windows/windows_vr_shell.h` instead
|
||||
of staying inline in `src/main.cpp`, but broader retained Win32 shell state
|
||||
is still open
|
||||
- prepared-file background work now runs through an `AppRuntime`-owned worker
|
||||
queue instead of a retained static worker in `src/app_events.cpp`
|
||||
- canvas async import/export/save/open background work now also runs through an
|
||||
|
||||
@@ -11,18 +11,12 @@
|
||||
#include "legacy_document_export_services.h"
|
||||
#include "legacy_document_layer_services.h"
|
||||
#include "legacy_document_session_services.h"
|
||||
#include "legacy_preference_storage.h"
|
||||
#include "legacy_ui_overlay_services.h"
|
||||
#include "node_dialog_open.h"
|
||||
#include "node_dialog_browse.h"
|
||||
#include "node_dialog_resize.h"
|
||||
#include "node_dialog_cloud.h"
|
||||
#include "node_about.h"
|
||||
#include "node_changelog.h"
|
||||
#include "node_usermanual.h"
|
||||
#include "node_dialog_export_ppbr.h"
|
||||
#include "node_remote_page.h"
|
||||
#include "node_shorcuts.h"
|
||||
|
||||
#include <codec_api.h>
|
||||
#define MP4V2_NO_STDINT_DEFS
|
||||
@@ -34,6 +28,14 @@
|
||||
#include "oculus_vr.h"
|
||||
#endif
|
||||
|
||||
namespace pp::panopainter {
|
||||
void open_usermanual_dialog(App& app);
|
||||
void open_changelog_dialog(App& app);
|
||||
void open_about_dialog(App& app);
|
||||
void open_whatsnew_dialog(App& app, bool force_show);
|
||||
void open_shortcuts_dialog(App& app);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool can_start_document_export(App& app, bool requires_license)
|
||||
@@ -208,80 +210,17 @@ std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
||||
|
||||
void App::dialog_usermanual()
|
||||
{
|
||||
auto* overlay_anchor = layout[main_id];
|
||||
if (!overlay_anchor) {
|
||||
LOG("User manual dialog open failed: main layout anchor is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeUserManual>(*this);
|
||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
||||
if (!overlay) {
|
||||
LOG("User manual dialog open failed: %s", overlay.status().message);
|
||||
return;
|
||||
}
|
||||
const auto overlay_handle = overlay.value();
|
||||
|
||||
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
||||
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
||||
(void)close_status;
|
||||
};
|
||||
|
||||
dialog->btn_ok->on_click = [close_dialog](Node*) {
|
||||
close_dialog();
|
||||
};
|
||||
pp::panopainter::open_usermanual_dialog(*this);
|
||||
}
|
||||
|
||||
void App::dialog_changelog()
|
||||
{
|
||||
auto* overlay_anchor = layout[main_id];
|
||||
if (!overlay_anchor) {
|
||||
LOG("Changelog dialog open failed: main layout anchor is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeChangelog>(*this);
|
||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
||||
if (!overlay) {
|
||||
LOG("Changelog dialog open failed: %s", overlay.status().message);
|
||||
return;
|
||||
}
|
||||
const auto overlay_handle = overlay.value();
|
||||
|
||||
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
||||
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
||||
(void)close_status;
|
||||
};
|
||||
|
||||
dialog->btn_ok->on_click = [close_dialog](Node*) {
|
||||
close_dialog();
|
||||
};
|
||||
pp::panopainter::open_changelog_dialog(*this);
|
||||
}
|
||||
|
||||
void App::dialog_about()
|
||||
{
|
||||
auto* overlay_anchor = layout[main_id];
|
||||
if (!overlay_anchor) {
|
||||
LOG("About dialog open failed: main layout anchor is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
auto dialog = pp::panopainter::make_legacy_overlay_node<NodeAbout>(*this);
|
||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
||||
if (!overlay) {
|
||||
LOG("About dialog open failed: %s", overlay.status().message);
|
||||
return;
|
||||
}
|
||||
const auto overlay_handle = overlay.value();
|
||||
|
||||
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
||||
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
||||
(void)close_status;
|
||||
};
|
||||
|
||||
dialog->btn_ok->on_click = [close_dialog](Node*) {
|
||||
close_dialog();
|
||||
};
|
||||
pp::panopainter::open_about_dialog(*this);
|
||||
}
|
||||
|
||||
void App::continue_document_workflow_after_optional_save(std::function<void()> action)
|
||||
@@ -841,68 +780,10 @@ void App::dialog_export_mp4()
|
||||
|
||||
void App::dialog_whatsnew(bool force_show)
|
||||
{
|
||||
auto* overlay_anchor = layout[main_id];
|
||||
if (!overlay_anchor) {
|
||||
LOG("What's new dialog open failed: main layout anchor is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto overlay_handle = std::make_shared<pp::ui::NodeHandle>();
|
||||
const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr<NodeRemotePage>& page) {
|
||||
if (overlay_handle->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*overlay_anchor, page);
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
*overlay_handle = overlay.value();
|
||||
};
|
||||
const auto close_overlay = [overlay_anchor, overlay_handle]() {
|
||||
if (!overlay_handle->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto close_status = pp::panopainter::close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
|
||||
(void)close_status;
|
||||
*overlay_handle = {};
|
||||
};
|
||||
|
||||
auto whatsnew = std::make_shared<NodeRemotePage>();
|
||||
whatsnew->set_manager(&layout);
|
||||
whatsnew->init();
|
||||
std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build);
|
||||
whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) {
|
||||
if (success)
|
||||
{
|
||||
int last_id = pp::panopainter::legacy_whatsnew_id_or(0);
|
||||
if (force_show || (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id))
|
||||
{
|
||||
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
|
||||
if (!force_show)
|
||||
open_overlay(whatsnew);
|
||||
}
|
||||
}
|
||||
});
|
||||
whatsnew->add_button("Reload", 120, [whatsnew](Node*) {
|
||||
whatsnew->reload();
|
||||
});
|
||||
whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) {
|
||||
pp::panopainter::clear_legacy_whatsnew_id();
|
||||
pp::panopainter::save_legacy_preferences();
|
||||
close_overlay();
|
||||
});
|
||||
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
|
||||
pp::panopainter::set_legacy_whatsnew_id(whatsnew->m_page_id);
|
||||
pp::panopainter::save_legacy_preferences();
|
||||
close_overlay();
|
||||
});
|
||||
if (force_show)
|
||||
open_overlay(whatsnew);
|
||||
pp::panopainter::open_whatsnew_dialog(*this, force_show);
|
||||
}
|
||||
|
||||
void App::dialog_shortcuts()
|
||||
{
|
||||
(void)pp::panopainter::add_legacy_overlay_node<NodeShortcuts>(*this);
|
||||
pp::panopainter::open_shortcuts_dialog(*this);
|
||||
}
|
||||
|
||||
128
src/app_dialogs_info_openers.cpp
Normal file
128
src/app_dialogs_info_openers.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "legacy_preference_storage.h"
|
||||
#include "legacy_ui_overlay_services.h"
|
||||
#include "node_about.h"
|
||||
#include "node_changelog.h"
|
||||
#include "node_remote_page.h"
|
||||
#include "node_shorcuts.h"
|
||||
#include "node_usermanual.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename DialogNode>
|
||||
void open_info_dialog(App& app, const char* log_name)
|
||||
{
|
||||
auto* overlay_anchor = app.layout[app.main_id];
|
||||
if (!overlay_anchor) {
|
||||
LOG("%s dialog open failed: main layout anchor is missing", log_name);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dialog = make_legacy_overlay_node<DialogNode>(app);
|
||||
const auto overlay = open_legacy_overlay_node_with_handle(*overlay_anchor, dialog);
|
||||
if (!overlay) {
|
||||
LOG("%s dialog open failed: %s", log_name, overlay.status().message);
|
||||
return;
|
||||
}
|
||||
const auto overlay_handle = overlay.value();
|
||||
|
||||
const auto close_dialog = [overlay_anchor, overlay_handle]() {
|
||||
const auto close_status = close_legacy_overlay_node(*overlay_anchor, overlay_handle);
|
||||
(void)close_status;
|
||||
};
|
||||
|
||||
dialog->btn_ok->on_click = [close_dialog](Node*) {
|
||||
close_dialog();
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void open_usermanual_dialog(App& app)
|
||||
{
|
||||
open_info_dialog<NodeUserManual>(app, "User manual");
|
||||
}
|
||||
|
||||
void open_changelog_dialog(App& app)
|
||||
{
|
||||
open_info_dialog<NodeChangelog>(app, "Changelog");
|
||||
}
|
||||
|
||||
void open_about_dialog(App& app)
|
||||
{
|
||||
open_info_dialog<NodeAbout>(app, "About");
|
||||
}
|
||||
|
||||
void open_whatsnew_dialog(App& app, bool force_show)
|
||||
{
|
||||
auto* overlay_anchor = app.layout[app.main_id];
|
||||
if (!overlay_anchor) {
|
||||
LOG("What's new dialog open failed: main layout anchor is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto overlay_handle = std::make_shared<pp::ui::NodeHandle>();
|
||||
const auto open_overlay = [overlay_anchor, overlay_handle](const std::shared_ptr<NodeRemotePage>& page) {
|
||||
if (overlay_handle->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto overlay = open_legacy_overlay_node_with_handle(*overlay_anchor, page);
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
*overlay_handle = overlay.value();
|
||||
};
|
||||
const auto close_overlay = [overlay_anchor, overlay_handle]() {
|
||||
if (!overlay_handle->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto close_status = close_legacy_overlay_node(*overlay_anchor, *overlay_handle);
|
||||
(void)close_status;
|
||||
*overlay_handle = {};
|
||||
};
|
||||
|
||||
auto whatsnew = std::make_shared<NodeRemotePage>();
|
||||
whatsnew->set_manager(&app.layout);
|
||||
whatsnew->init();
|
||||
std::string url = fmt::format("https://panopainter.com/app-content/whatsnew/?version={}", g_version_build);
|
||||
whatsnew->load_url(url, [whatsnew, force_show, open_overlay](bool success) {
|
||||
if (success)
|
||||
{
|
||||
int last_id = legacy_whatsnew_id_or(0);
|
||||
if (force_show || (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id))
|
||||
{
|
||||
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
|
||||
if (!force_show)
|
||||
open_overlay(whatsnew);
|
||||
}
|
||||
}
|
||||
});
|
||||
whatsnew->add_button("Reload", 120, [whatsnew](Node*) {
|
||||
whatsnew->reload();
|
||||
});
|
||||
whatsnew->add_button("Read Later", 120, [whatsnew, close_overlay](Node*) {
|
||||
clear_legacy_whatsnew_id();
|
||||
save_legacy_preferences();
|
||||
close_overlay();
|
||||
});
|
||||
whatsnew->add_button("Close", 100, [whatsnew, close_overlay](Node*) {
|
||||
set_legacy_whatsnew_id(whatsnew->m_page_id);
|
||||
save_legacy_preferences();
|
||||
close_overlay();
|
||||
});
|
||||
if (force_show)
|
||||
open_overlay(whatsnew);
|
||||
}
|
||||
|
||||
void open_shortcuts_dialog(App& app)
|
||||
{
|
||||
(void)add_legacy_overlay_node<NodeShortcuts>(app);
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "legacy_canvas_stroke_composite_services.h"
|
||||
#include "legacy_canvas_stroke_erase_services.h"
|
||||
#include "shader.h"
|
||||
|
||||
#include <array>
|
||||
@@ -130,6 +132,40 @@ struct LegacyCanvasDrawMergeLayerPathExecution {
|
||||
std::function<void(int, float)> draw_frame;
|
||||
};
|
||||
|
||||
struct LegacyCanvasDrawMergeLayerPathGlUniforms {
|
||||
LegacyStrokeEraseUniforms temporary_erase;
|
||||
LegacyStrokeCompositeUniforms temporary_paint;
|
||||
LegacyCanvasDrawMergeTextureAlphaUniforms layer_texture;
|
||||
LegacyCanvasDrawMergeLayerBlendUniforms blend;
|
||||
bool use_nearest_sampler = false;
|
||||
bool use_dual_texture = false;
|
||||
};
|
||||
|
||||
struct LegacyCanvasDrawMergeLayerPathGlExecution {
|
||||
std::function<void()> bind_blender_framebuffer;
|
||||
std::function<void()> clear_blender_framebuffer;
|
||||
std::function<void()> unbind_blender_framebuffer;
|
||||
std::function<void(int)> bind_sampler;
|
||||
std::function<void(int)> bind_nearest_sampler;
|
||||
std::function<void(int)> bind_stencil_sampler;
|
||||
std::function<void(int)> set_active_texture_unit;
|
||||
std::function<void()> bind_temporary_texture;
|
||||
std::function<void()> unbind_temporary_texture;
|
||||
std::function<void()> bind_smask_texture;
|
||||
std::function<void()> unbind_smask_texture;
|
||||
std::function<void()> bind_temporary_dual_texture;
|
||||
std::function<void()> unbind_temporary_dual_texture;
|
||||
std::function<void()> bind_pattern_texture;
|
||||
std::function<void()> draw_face;
|
||||
std::function<void()> bind_blender_texture;
|
||||
std::function<void()> unbind_blender_texture;
|
||||
std::function<void()> bind_destination_texture;
|
||||
std::function<void()> unbind_destination_texture;
|
||||
std::function<void()> copy_destination_framebuffer;
|
||||
std::function<void()> draw_debug_outline;
|
||||
std::function<void(int, float)> draw_frame;
|
||||
};
|
||||
|
||||
struct LegacyCanvasDrawLayerVisit {
|
||||
size_t layer_index = 0;
|
||||
int plane_index = 0;
|
||||
@@ -679,6 +715,102 @@ inline void execute_legacy_canvas_draw_merge_layer_path(
|
||||
execution.draw_debug_outline();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline LegacyCanvasDrawMergeLayerPathExecution make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
||||
const LegacyCanvasDrawMergeLayerPathGlUniforms& uniforms,
|
||||
const LegacyCanvasDrawMergeLayerPathGlExecution& execution)
|
||||
{
|
||||
return {
|
||||
.bind_blender_framebuffer = execution.bind_blender_framebuffer,
|
||||
.clear_blender_framebuffer = execution.clear_blender_framebuffer,
|
||||
.unbind_blender_framebuffer = execution.unbind_blender_framebuffer,
|
||||
.prepare_temporary_erase = [uniforms, execution] {
|
||||
execution.bind_sampler(0);
|
||||
execution.bind_sampler(1);
|
||||
execution.bind_sampler(2);
|
||||
setup_legacy_stroke_erase_shader(uniforms.temporary_erase);
|
||||
execution.set_active_texture_unit(1);
|
||||
execution.bind_temporary_texture();
|
||||
execution.set_active_texture_unit(2);
|
||||
execution.bind_smask_texture();
|
||||
},
|
||||
.cleanup_temporary_erase = [execution] {
|
||||
execution.set_active_texture_unit(2);
|
||||
execution.unbind_smask_texture();
|
||||
execution.set_active_texture_unit(1);
|
||||
execution.unbind_temporary_texture();
|
||||
},
|
||||
.prepare_temporary_paint = [uniforms, execution] {
|
||||
execution.bind_sampler(0);
|
||||
execution.bind_sampler(1);
|
||||
execution.bind_sampler(2);
|
||||
execution.bind_sampler(3);
|
||||
execution.bind_stencil_sampler(4);
|
||||
setup_legacy_stroke_composite_shader(uniforms.temporary_paint);
|
||||
execution.set_active_texture_unit(1);
|
||||
execution.bind_temporary_texture();
|
||||
execution.set_active_texture_unit(2);
|
||||
execution.bind_smask_texture();
|
||||
execution.set_active_texture_unit(3);
|
||||
if (uniforms.use_dual_texture) {
|
||||
execution.bind_temporary_dual_texture();
|
||||
}
|
||||
execution.set_active_texture_unit(4);
|
||||
execution.bind_pattern_texture();
|
||||
},
|
||||
.cleanup_temporary_paint = [uniforms, execution] {
|
||||
execution.set_active_texture_unit(3);
|
||||
if (uniforms.use_dual_texture) {
|
||||
execution.unbind_temporary_dual_texture();
|
||||
}
|
||||
execution.set_active_texture_unit(2);
|
||||
execution.unbind_smask_texture();
|
||||
execution.set_active_texture_unit(1);
|
||||
execution.unbind_temporary_texture();
|
||||
},
|
||||
.prepare_layer_texture = [uniforms, execution] {
|
||||
if (uniforms.use_nearest_sampler) {
|
||||
execution.bind_nearest_sampler(0);
|
||||
} else {
|
||||
execution.bind_sampler(0);
|
||||
}
|
||||
setup_legacy_canvas_draw_merge_texture_alpha_shader(uniforms.layer_texture);
|
||||
},
|
||||
.cleanup_layer_texture = [] {
|
||||
},
|
||||
.draw_blend = [uniforms, execution] {
|
||||
execute_legacy_canvas_draw_merge_layer_blend(
|
||||
uniforms.blend,
|
||||
{
|
||||
.unbind_merge_framebuffer = execution.unbind_blender_framebuffer,
|
||||
.bind_samplers = [execution] {
|
||||
execution.bind_sampler(0);
|
||||
execution.bind_sampler(2);
|
||||
},
|
||||
.bind_merge_texture = [execution] {
|
||||
execution.set_active_texture_unit(0);
|
||||
execution.bind_blender_texture();
|
||||
},
|
||||
.bind_destination_texture = [execution] {
|
||||
execution.set_active_texture_unit(2);
|
||||
execution.bind_destination_texture();
|
||||
},
|
||||
.copy_destination_framebuffer = execution.copy_destination_framebuffer,
|
||||
.draw = execution.draw_face,
|
||||
.unbind_destination_texture = [execution] {
|
||||
execution.set_active_texture_unit(2);
|
||||
execution.unbind_destination_texture();
|
||||
},
|
||||
.unbind_merge_texture = [execution] {
|
||||
execution.set_active_texture_unit(0);
|
||||
execution.unbind_blender_texture();
|
||||
},
|
||||
});
|
||||
},
|
||||
.draw_debug_outline = execution.draw_debug_outline,
|
||||
.draw_frame = execution.draw_frame,
|
||||
};
|
||||
}
|
||||
|
||||
template <typename PlanOnionRange, typename ShouldDrawPlane, typename VisitLayerPlane, typename LogOnionRangeFailure>
|
||||
inline void execute_legacy_canvas_draw_layer_traversal(
|
||||
size_t layer_count,
|
||||
|
||||
113
src/main.cpp
113
src/main.cpp
@@ -7,20 +7,18 @@
|
||||
#include "app.h"
|
||||
#include "canvas.h"
|
||||
#include "keymap.h"
|
||||
#include "hmd.h"
|
||||
#include "legacy_gl_runtime_dispatch.h"
|
||||
#include "legacy_preference_storage.h"
|
||||
#include "renderer_gl/opengl_capabilities.h"
|
||||
#include "platform_windows/windows_platform_services.h"
|
||||
#include "platform_windows/windows_splash.h"
|
||||
#include "platform_windows/windows_stylus_input.h"
|
||||
#include "platform_windows/windows_vr_shell.h"
|
||||
#include "../resource.h"
|
||||
|
||||
#include <shellscalingapi.h>
|
||||
#include <WbemCli.h>
|
||||
#include <deque>
|
||||
#include <chrono>
|
||||
|
||||
#include "wacom.h"
|
||||
#include "abr.h"
|
||||
|
||||
@@ -32,9 +30,7 @@
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <stop_token>
|
||||
|
||||
#define WM_USER_CLOSE (WM_USER + 1)
|
||||
#define WM_USER_WAKEUP (WM_USER + 2)
|
||||
@@ -55,11 +51,8 @@ struct RetainedState
|
||||
bool keys[256]{};
|
||||
std::map<kKey, int> vkey_map;
|
||||
wchar_t window_title[512]{};
|
||||
std::jthread hmd_renderer;
|
||||
bool sandboxed = false;
|
||||
std::mutex hmd_render_mutex;
|
||||
std::condition_variable hmd_render_cv;
|
||||
std::unique_ptr<Vive> vive;
|
||||
pp::platform::windows::VrShellState vr;
|
||||
};
|
||||
|
||||
RetainedState& retained_state()
|
||||
@@ -125,9 +118,7 @@ void pp_windows_enqueue_main_task(std::packaged_task<void()> task)
|
||||
enqueue_main_task_bridge(std::move(task));
|
||||
}
|
||||
|
||||
std::atomic<int> vr_frames{0};
|
||||
std::atomic<int> running{-1};
|
||||
std::atomic_bool vr_running{false};
|
||||
|
||||
#ifdef USE_RENDERDOC
|
||||
RENDERDOC_API_1_4_0* rdoc_api = NULL;
|
||||
@@ -230,8 +221,8 @@ void win32_update_stylus(float dt)
|
||||
void win32_update_fps(int frames)
|
||||
{
|
||||
static wchar_t title_fps[512];
|
||||
const int vr_fps = vr_frames.load(std::memory_order_relaxed);
|
||||
auto& state = retained_state();
|
||||
const int vr_fps = pp::platform::windows::current_vr_fps(state.vr);
|
||||
if (App::I->vr_active)
|
||||
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", state.window_title, frames, vr_fps);
|
||||
else
|
||||
@@ -520,101 +511,13 @@ void init_vk_map()
|
||||
bool win32_vr_start()
|
||||
{
|
||||
auto& state = retained_state();
|
||||
if (state.sandboxed)
|
||||
return false;
|
||||
|
||||
state.vive = std::make_unique<Vive>();
|
||||
state.vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I->vr_draw(proj, view, pose); };
|
||||
if (!state.vive->Initialize())
|
||||
{
|
||||
state.vive.reset();
|
||||
LOG("VR: failed to initialize vive");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.hmd_renderer.joinable())
|
||||
{
|
||||
state.hmd_renderer.request_stop();
|
||||
state.hmd_renderer.join();
|
||||
}
|
||||
state.hmd_renderer = std::jthread([&state](std::stop_token stop_token) {
|
||||
if (!state.vive)
|
||||
return;
|
||||
|
||||
BT_SetTerminate();
|
||||
LOG("start hmd render thread");
|
||||
App::I->has_vr = true;
|
||||
vr_running.store(true, std::memory_order_relaxed);
|
||||
|
||||
state.vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1,
|
||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
||||
state.vive->on_button = std::bind(&App::vr_digital, App::I, std::placeholders::_1,
|
||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
||||
|
||||
App::I->render_task([] {
|
||||
App::I->vr_draw_ui();
|
||||
});
|
||||
|
||||
const float target_tick_rate = 90;
|
||||
auto t0 = GetTickCount64();
|
||||
float one_sec_timer = 0;
|
||||
int frames = 0;
|
||||
while (!stop_token.stop_requested() && vr_running.load(std::memory_order_relaxed) && running.load(std::memory_order_relaxed) == 1 && state.vive->Valid())
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(state.hmd_render_mutex);
|
||||
auto t1 = GetTickCount64();
|
||||
float dt = (float)(t1 - t0) / 1000.0f;
|
||||
|
||||
one_sec_timer += dt;
|
||||
if (one_sec_timer >= 1.f)
|
||||
{
|
||||
one_sec_timer = 0;
|
||||
vr_frames.store(frames, std::memory_order_relaxed);
|
||||
frames = 0;
|
||||
}
|
||||
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;
|
||||
App::I->vr_update(dt);
|
||||
|
||||
if (vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
|
||||
{
|
||||
App::I->render_task([&state] {
|
||||
state.vive->Draw();
|
||||
});
|
||||
}
|
||||
|
||||
const int framerate = (1.f / target_tick_rate) * 1000;
|
||||
const int diff = framerate - (t1 - t0);
|
||||
//state.hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
||||
t0 = t1;
|
||||
}
|
||||
App::I->vr_active = false;
|
||||
App::I->has_vr = false;
|
||||
vr_running.store(false, std::memory_order_relaxed);
|
||||
state.vive->Terminate();
|
||||
LOG("hmd renderer terminated");
|
||||
});
|
||||
return true;
|
||||
return pp::platform::windows::start_vr_shell(state.vr, state.sandboxed, running);
|
||||
}
|
||||
|
||||
void win32_vr_stop()
|
||||
{
|
||||
auto& state = retained_state();
|
||||
if (state.vive)
|
||||
{
|
||||
vr_running.store(false, std::memory_order_relaxed);
|
||||
if (state.hmd_renderer.joinable())
|
||||
{
|
||||
state.hmd_renderer.request_stop();
|
||||
state.hmd_renderer.join();
|
||||
}
|
||||
state.vive->Terminate();
|
||||
state.vive.reset();
|
||||
}
|
||||
pp::platform::windows::stop_vr_shell(state.vr);
|
||||
}
|
||||
|
||||
void win32_save_window_state()
|
||||
@@ -947,11 +850,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
{
|
||||
case WM_USER_CLOSE:
|
||||
running.store(0, std::memory_order_relaxed);
|
||||
if (state.hmd_renderer.joinable())
|
||||
{
|
||||
state.hmd_renderer.request_stop();
|
||||
state.hmd_renderer.join();
|
||||
}
|
||||
pp::platform::windows::request_stop_and_join_vr_thread(state.vr);
|
||||
App::I->runtime().ui_thread_stop();
|
||||
App::I->runtime().render_thread_stop();
|
||||
App::I->terminate();
|
||||
|
||||
@@ -475,14 +475,62 @@ void NodeCanvas::draw()
|
||||
plane_index,
|
||||
m_canvas->m_layers[layer_index]->m_opacity);
|
||||
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_path(
|
||||
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||
m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||
use_blend,
|
||||
visit.first_frame,
|
||||
visit.last_frame,
|
||||
[&](int frame) {
|
||||
return pp::app::animation_onion_frame_alpha(onion_range, frame);
|
||||
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
||||
if (b->m_pattern_flipx)
|
||||
patt_scale.x *= -1.f;
|
||||
if (b->m_pattern_flipy)
|
||||
patt_scale.y *= -1.f;
|
||||
|
||||
const auto layer_path_execution = pp::panopainter::make_legacy_canvas_draw_merge_layer_path_gl_execution(
|
||||
{
|
||||
.temporary_erase = {
|
||||
.mvp = plane_mvp_z,
|
||||
.texture_slot = 0,
|
||||
.stroke_texture_slot = 1,
|
||||
.mask_texture_slot = 2,
|
||||
.mask_enabled = m_canvas->m_smask_active,
|
||||
},
|
||||
.temporary_paint = {
|
||||
.resolution = Canvas::I->m_size,
|
||||
.pattern = {
|
||||
.scale = patt_scale,
|
||||
.invert = static_cast<float>(b->m_pattern_invert),
|
||||
.brightness = b->m_pattern_brightness,
|
||||
.contrast = b->m_pattern_contrast,
|
||||
.depth = b->m_pattern_depth,
|
||||
.blend_mode = b->m_pattern_blend_mode,
|
||||
.offset = Canvas::I->m_pattern_offset,
|
||||
},
|
||||
.mvp = plane_mvp_z,
|
||||
.layer_alpha = 1.0f,
|
||||
.alpha_lock = m_canvas->m_layers[layer_index]->m_alpha_locked,
|
||||
.mask_enabled = m_canvas->m_smask_active,
|
||||
.use_fragcoord = false,
|
||||
.blend_mode = b->m_blend_mode,
|
||||
.use_dual = b->m_dual_enabled,
|
||||
.dual_blend_mode = b->m_dual_blend_mode,
|
||||
.dual_alpha = b->m_dual_opacity,
|
||||
.use_pattern = b->m_pattern_enabled && !b->m_pattern_eachsample,
|
||||
},
|
||||
.layer_texture = {
|
||||
.mvp = plane_mvp_z,
|
||||
.texture_slot = 0,
|
||||
.alpha = 1.f,
|
||||
.highlight = m_canvas->m_layers[layer_index]->m_hightlight,
|
||||
},
|
||||
.blend = {
|
||||
.shader = {
|
||||
.mvp = glm::ortho(-1, 1, -1, 1),
|
||||
.texture_slot = 0,
|
||||
.destination_texture_slot = 2,
|
||||
.use_destination_texture = copy_blend_destination,
|
||||
.blend_mode = m_canvas->m_layers[layer_index]->m_blend_mode,
|
||||
.alpha = 1.f,
|
||||
},
|
||||
.copy_destination = copy_blend_destination,
|
||||
},
|
||||
.use_nearest_sampler = m_canvas->m_cam_fov < 20.f,
|
||||
.use_dual_texture = b->m_dual_enabled,
|
||||
},
|
||||
{
|
||||
.bind_blender_framebuffer = [&] {
|
||||
@@ -494,139 +542,65 @@ void NodeCanvas::draw()
|
||||
.unbind_blender_framebuffer = [&] {
|
||||
m_blender_rtt.unbindFramebuffer();
|
||||
},
|
||||
.prepare_temporary_erase = [&] {
|
||||
m_sampler.bind(0);
|
||||
m_sampler.bind(1);
|
||||
m_sampler.bind(2);
|
||||
|
||||
//ShaderManager::u_vec2(kShaderUniform::Resolution, zw(m_canvas->m_box) / zoom);
|
||||
//ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index]->m_alpha_locked);
|
||||
pp::panopainter::setup_legacy_stroke_erase_shader(
|
||||
pp::panopainter::LegacyStrokeEraseUniforms {
|
||||
.mvp = plane_mvp_z,
|
||||
.texture_slot = 0,
|
||||
.stroke_texture_slot = 1,
|
||||
.mask_texture_slot = 2,
|
||||
.mask_enabled = m_canvas->m_smask_active,
|
||||
});
|
||||
set_active_texture_unit(1);
|
||||
m_canvas->m_tmp[plane_index].bindTexture();
|
||||
set_active_texture_unit(2);
|
||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
||||
.bind_sampler = [&](int unit) {
|
||||
m_sampler.bind(unit);
|
||||
},
|
||||
.cleanup_temporary_erase = [&] {
|
||||
set_active_texture_unit(2);
|
||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||
set_active_texture_unit(1);
|
||||
.bind_nearest_sampler = [&](int unit) {
|
||||
m_sampler_nearest.bind(unit);
|
||||
},
|
||||
.bind_stencil_sampler = [&](int unit) {
|
||||
m_sampler_stencil.bind(unit);
|
||||
},
|
||||
.set_active_texture_unit = [&](int unit) {
|
||||
set_active_texture_unit(unit);
|
||||
},
|
||||
.bind_temporary_texture = [&] {
|
||||
m_canvas->m_tmp[plane_index].bindTexture();
|
||||
},
|
||||
.unbind_temporary_texture = [&] {
|
||||
m_canvas->m_tmp[plane_index].unbindTexture();
|
||||
},
|
||||
.prepare_temporary_paint = [&] {
|
||||
m_sampler.bind(0);
|
||||
m_sampler.bind(1);
|
||||
m_sampler.bind(2);
|
||||
m_sampler.bind(3);
|
||||
m_sampler_stencil.bind(4);
|
||||
|
||||
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
||||
if (b->m_pattern_flipx) patt_scale.x *= -1.f;
|
||||
if (b->m_pattern_flipy) patt_scale.y *= -1.f;
|
||||
|
||||
pp::panopainter::setup_legacy_stroke_composite_shader(
|
||||
pp::panopainter::LegacyStrokeCompositeUniforms {
|
||||
.resolution = Canvas::I->m_size,
|
||||
.pattern = {
|
||||
.scale = patt_scale,
|
||||
.invert = static_cast<float>(b->m_pattern_invert),
|
||||
.brightness = b->m_pattern_brightness,
|
||||
.contrast = b->m_pattern_contrast,
|
||||
.depth = b->m_pattern_depth,
|
||||
.blend_mode = b->m_pattern_blend_mode,
|
||||
.offset = Canvas::I->m_pattern_offset,
|
||||
},
|
||||
.mvp = plane_mvp_z,
|
||||
.layer_alpha = 1.0f,
|
||||
.alpha_lock = m_canvas->m_layers[layer_index]->m_alpha_locked,
|
||||
.mask_enabled = m_canvas->m_smask_active,
|
||||
.use_fragcoord = false,
|
||||
.blend_mode = b->m_blend_mode,
|
||||
.use_dual = b->m_dual_enabled,
|
||||
.dual_blend_mode = b->m_dual_blend_mode,
|
||||
.dual_alpha = b->m_dual_opacity,
|
||||
.use_pattern = b->m_pattern_enabled && !b->m_pattern_eachsample,
|
||||
});
|
||||
|
||||
set_active_texture_unit(1);
|
||||
m_canvas->m_tmp[plane_index].bindTexture();
|
||||
set_active_texture_unit(2);
|
||||
.bind_smask_texture = [&] {
|
||||
m_canvas->m_smask.rtt(plane_index).bindTexture();
|
||||
set_active_texture_unit(3);
|
||||
if (b->m_dual_enabled)
|
||||
m_canvas->m_tmp_dual[plane_index].bindTexture();
|
||||
set_active_texture_unit(4);
|
||||
},
|
||||
.unbind_smask_texture = [&] {
|
||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||
},
|
||||
.bind_temporary_dual_texture = [&] {
|
||||
m_canvas->m_tmp_dual[plane_index].bindTexture();
|
||||
},
|
||||
.unbind_temporary_dual_texture = [&] {
|
||||
m_canvas->m_tmp_dual[plane_index].unbindTexture();
|
||||
},
|
||||
.bind_pattern_texture = [&] {
|
||||
b->m_pattern_texture ?
|
||||
b->m_pattern_texture->bind() :
|
||||
unbind_texture_2d();
|
||||
},
|
||||
.cleanup_temporary_paint = [&] {
|
||||
set_active_texture_unit(3);
|
||||
if (b->m_dual_enabled)
|
||||
m_canvas->m_tmp_dual[plane_index].unbindTexture();
|
||||
set_active_texture_unit(2);
|
||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||
set_active_texture_unit(1);
|
||||
m_canvas->m_tmp[plane_index].unbindTexture();
|
||||
},
|
||||
.prepare_layer_texture = [&] {
|
||||
m_canvas->m_cam_fov < 20.f ? m_sampler_nearest.bind(0) : m_sampler.bind(0);
|
||||
pp::panopainter::setup_legacy_canvas_draw_merge_texture_alpha_shader(
|
||||
pp::panopainter::LegacyCanvasDrawMergeTextureAlphaUniforms {
|
||||
.mvp = plane_mvp_z,
|
||||
.texture_slot = 0,
|
||||
.alpha = 1.f,
|
||||
.highlight = m_canvas->m_layers[layer_index]->m_hightlight,
|
||||
});
|
||||
},
|
||||
.cleanup_layer_texture = [&] {
|
||||
},
|
||||
.draw_blend = [&] {
|
||||
m_sampler.bind(0);
|
||||
m_sampler.bind(2);
|
||||
|
||||
pp::panopainter::setup_legacy_canvas_draw_merge_texture_blend_shader(
|
||||
pp::panopainter::LegacyCanvasDrawMergeTextureBlendUniforms {
|
||||
.mvp = glm::ortho(-1, 1, -1, 1),
|
||||
.texture_slot = 0,
|
||||
.destination_texture_slot = 2,
|
||||
.use_destination_texture = copy_blend_destination,
|
||||
.blend_mode = m_canvas->m_layers[layer_index]->m_blend_mode,
|
||||
.alpha = 1.f,
|
||||
});
|
||||
|
||||
set_active_texture_unit(0);
|
||||
m_blender_rtt.bindTexture();
|
||||
if (copy_blend_destination)
|
||||
{
|
||||
set_active_texture_unit(2);
|
||||
m_blender_bg.bind();
|
||||
copy_framebuffer_to_texture_2d(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
m_blender_bg.size().x,
|
||||
m_blender_bg.size().y);
|
||||
}
|
||||
|
||||
.draw_face = [&] {
|
||||
m_face_plane.draw_fill();
|
||||
|
||||
if (copy_blend_destination)
|
||||
{
|
||||
set_active_texture_unit(2);
|
||||
m_blender_bg.unbind();
|
||||
}
|
||||
set_active_texture_unit(0);
|
||||
},
|
||||
.bind_blender_texture = [&] {
|
||||
m_blender_rtt.bindTexture();
|
||||
},
|
||||
.unbind_blender_texture = [&] {
|
||||
m_blender_rtt.unbindTexture();
|
||||
},
|
||||
.bind_destination_texture = [&] {
|
||||
m_blender_bg.bind();
|
||||
},
|
||||
.unbind_destination_texture = [&] {
|
||||
m_blender_bg.unbind();
|
||||
},
|
||||
.copy_destination_framebuffer = [&] {
|
||||
copy_framebuffer_to_texture_2d(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
m_blender_bg.size().x,
|
||||
m_blender_bg.size().y);
|
||||
},
|
||||
.draw_debug_outline =
|
||||
#ifdef _DEBUG
|
||||
[&] {
|
||||
@@ -648,6 +622,17 @@ void NodeCanvas::draw()
|
||||
#endif
|
||||
.draw_frame = draw_layer_frame,
|
||||
});
|
||||
|
||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_path(
|
||||
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||
m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||
use_blend,
|
||||
visit.first_frame,
|
||||
visit.last_frame,
|
||||
[&](int frame) {
|
||||
return pp::app::animation_onion_frame_alpha(onion_range, frame);
|
||||
},
|
||||
layer_path_execution);
|
||||
},
|
||||
[&](const char* message) {
|
||||
LOG("NodeCanvas onion frame range failed: %s", message);
|
||||
|
||||
141
src/platform_windows/windows_vr_shell.h
Normal file
141
src/platform_windows/windows_vr_shell.h
Normal file
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include "app.h"
|
||||
#include "hmd.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace pp::platform::windows {
|
||||
|
||||
struct VrShellState final {
|
||||
std::jthread hmd_renderer;
|
||||
std::mutex hmd_render_mutex;
|
||||
std::condition_variable hmd_render_cv;
|
||||
std::unique_ptr<Vive> vive;
|
||||
std::atomic<int> vr_frames{0};
|
||||
std::atomic_bool vr_running{false};
|
||||
};
|
||||
|
||||
inline int current_vr_fps(const VrShellState& state)
|
||||
{
|
||||
return state.vr_frames.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline void request_stop_and_join_vr_thread(VrShellState& state)
|
||||
{
|
||||
if (state.hmd_renderer.joinable())
|
||||
{
|
||||
state.hmd_renderer.request_stop();
|
||||
state.hmd_renderer.join();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>& running)
|
||||
{
|
||||
if (sandboxed)
|
||||
return false;
|
||||
|
||||
state.vive = std::make_unique<Vive>();
|
||||
state.vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) {
|
||||
App::I->vr_draw(proj, view, pose);
|
||||
};
|
||||
if (!state.vive->Initialize())
|
||||
{
|
||||
state.vive.reset();
|
||||
LOG("VR: failed to initialize vive");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.hmd_renderer.joinable())
|
||||
{
|
||||
state.hmd_renderer.request_stop();
|
||||
state.hmd_renderer.join();
|
||||
}
|
||||
|
||||
state.hmd_renderer = std::jthread([&state, &running](std::stop_token stop_token) {
|
||||
if (!state.vive)
|
||||
return;
|
||||
|
||||
BT_SetTerminate();
|
||||
LOG("start hmd render thread");
|
||||
App::I->has_vr = 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,
|
||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
||||
state.vive->on_button = std::bind(&App::vr_digital, App::I, std::placeholders::_1,
|
||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
|
||||
|
||||
App::I->render_task([] {
|
||||
App::I->vr_draw_ui();
|
||||
});
|
||||
|
||||
const float target_tick_rate = 90;
|
||||
auto t0 = GetTickCount64();
|
||||
float one_sec_timer = 0;
|
||||
int frames = 0;
|
||||
while (!stop_token.stop_requested() &&
|
||||
state.vr_running.load(std::memory_order_relaxed) &&
|
||||
running.load(std::memory_order_relaxed) == 1 &&
|
||||
state.vive->Valid())
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(state.hmd_render_mutex);
|
||||
auto t1 = GetTickCount64();
|
||||
float dt = (float)(t1 - t0) / 1000.0f;
|
||||
|
||||
one_sec_timer += dt;
|
||||
if (one_sec_timer >= 1.f)
|
||||
{
|
||||
one_sec_timer = 0;
|
||||
state.vr_frames.store(frames, std::memory_order_relaxed);
|
||||
frames = 0;
|
||||
}
|
||||
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;
|
||||
App::I->vr_update(dt);
|
||||
|
||||
if (state.vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
|
||||
{
|
||||
App::I->render_task([&state] {
|
||||
state.vive->Draw();
|
||||
});
|
||||
}
|
||||
|
||||
const int framerate = (1.f / target_tick_rate) * 1000;
|
||||
const int diff = framerate - (t1 - t0);
|
||||
(void)diff;
|
||||
//state.hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
||||
t0 = t1;
|
||||
}
|
||||
|
||||
App::I->vr_active = false;
|
||||
App::I->has_vr = false;
|
||||
state.vr_running.store(false, std::memory_order_relaxed);
|
||||
state.vive->Terminate();
|
||||
LOG("hmd renderer terminated");
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void stop_vr_shell(VrShellState& state)
|
||||
{
|
||||
if (state.vive)
|
||||
{
|
||||
state.vr_running.store(false, std::memory_order_relaxed);
|
||||
request_stop_and_join_vr_thread(state);
|
||||
state.vive->Terminate();
|
||||
state.vive.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user