From cb9d06c6dcfdd74751f3d106f417e464dc7bb044 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 16 Jun 2026 12:53:49 +0200 Subject: [PATCH] Extract edit menu and Windows/bootstrap preview seams --- cmake/PanoPainterSources.cmake | 1 + docs/modernization/roadmap.md | 27 +- docs/modernization/tasks.md | 23 +- src/app_layout.cpp | 30 +- src/app_layout_edit_menu.cpp | 47 +++ ...y_node_stroke_preview_execution_services.h | 92 ++++++ src/main.cpp | 281 +----------------- src/node_stroke_preview.cpp | 95 +----- .../windows_bootstrap_helpers.cpp | 223 +++++++++++++- .../windows_bootstrap_helpers.h | 57 ++++ .../windows_platform_services.cpp | 19 +- src/platform_windows/windows_splash.cpp | 7 +- 12 files changed, 493 insertions(+), 409 deletions(-) create mode 100644 src/app_layout_edit_menu.cpp create mode 100644 src/platform_windows/windows_bootstrap_helpers.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index d9110f3c..607318b8 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -99,6 +99,7 @@ set(PP_PANOPAINTER_APP_SOURCES src/app_layout.cpp src/app_layout_sidebar.cpp src/app_layout_main_toolbar.cpp + src/app_layout_edit_menu.cpp src/app_layout_about_layer_menu.cpp src/app_layout_file_menu.cpp src/app_layout_tools_menu.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 8fbdfefd..77e66544 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -80,12 +80,12 @@ What is still carrying too much live ownership: Current hotspot files: - `src/canvas.cpp`: 2645 lines -- `src/app_layout.cpp`: 743 lines +- `src/app_layout.cpp`: 717 lines - `src/canvas_modes.cpp`: 1798 lines - `src/node.cpp`: 1594 lines -- `src/main.cpp`: 1098 lines +- `src/main.cpp`: 847 lines - `src/node_panel_brush.cpp`: 1197 lines -- `src/node_stroke_preview.cpp`: 933 lines +- `src/node_stroke_preview.cpp`: 948 lines - `src/node_canvas.cpp`: 910 lines - `src/app.cpp`: 502 lines - `src/app_dialogs.cpp`: 142 lines @@ -142,7 +142,9 @@ Current architecture mismatches that must be treated as real blockers: binding and popup wiring now also live in `src/app_layout_sidebar.cpp` and `App::init_sidebar()` is now a thin call-through, while main-toolbar binding now also lives in `src/app_layout_main_toolbar.cpp` and - `App::init_toolbar_main()` is now a thin call-through, while the + `App::init_toolbar_main()` is now a thin call-through, while edit-menu + binding now also lives in `src/app_layout_edit_menu.cpp` and + `App::init_menu_edit()` is now a thin call-through, while the informational overlay opener family now also lives in `src/app_dialogs_info_openers.cpp` and the corresponding `App::dialog_*` entrypoints are thinner, while the export/video/PPBR dialog family now also @@ -186,12 +188,12 @@ Current architecture mismatches that must be treated as real blockers: sequencing, FPS title update/wakeup posting, stylus frame update, window preference save, and VR lifecycle wrappers now also live in `src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`, - while the Win32 startup/window/bootstrap path in `src/main.cpp` now also - routes through named local helper functions for runtime-data discovery, - startup-state initialization, window creation, pixel-format setup, GL loader - init, runtime-info logging, and core-context upgrade sequencing even though - that state still needs to move into platform-owned modules for the file to - shrink materially, + while the Win32 startup/window/bootstrap path now also lives under + `src/platform_windows/windows_bootstrap_helpers.*` for runtime-data + discovery, startup-state initialization, window creation, pixel-format + setup, GL loader init, runtime-info logging, and core-context upgrade + sequencing, which materially thins `src/main.cpp` even though broader window + procedure and retained shell flow still remain there, 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 @@ -207,7 +209,10 @@ Current architecture mismatches that must be treated as real blockers: document-IO cluster now lives in `src/legacy_canvas_document_io_services.cpp` and `NodeStrokePreview` render-target setup plus immediate-pass sequencing now route through retained preview execution helpers, even though the bridge - still owns worker-side readback flow and encoder-state label reads, while + still owns worker-side readback flow and encoder-state label reads, while the + main live-pass request assembly and framebuffer-copy setup now also route + through `src/legacy_node_stroke_preview_execution_services.h`, even though + broader preview-pass orchestration still lives in `src/node_stroke_preview.cpp`, while `Node` child attach/detach/reorder operations now route through named local helpers in `src/node.cpp`, which makes the scene-graph mutation paths easier to reason about without yet reducing the file or moving ownership into diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index e86426ee..85ee1b95 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -148,6 +148,11 @@ Current slice: target setup was removed from `render_to_image()` and the queued worker path, but the preview node still owns broader live-pass state and thread-facing orchestration. +- `NodeStrokePreview` main live-pass request assembly and preview framebuffer- + copy setup now also route through + `legacy_node_stroke_preview_execution_services.h`, which trims another + coherent pass-setup block from `src/node_stroke_preview.cpp`, but broader + preview-pass orchestration is still inline. - `NodeCanvas` merged-path per-plane merged-texture draw execution now also routes through `execute_legacy_canvas_draw_merge_layer_texture(...)`. - `NodeCanvas` merged-path and non-blend checkerboard background setup now also @@ -293,7 +298,7 @@ targets look like helpers under one old monolith. Status: In Progress Why now: -`src/app_layout.cpp` is still a 743-line mixed file that builds menus, +`src/app_layout.cpp` is still a 717-line mixed file that builds menus, attaches callbacks, computes planner inputs, and mutates UI state directly. Current slice: @@ -316,6 +321,10 @@ Current slice: - Main-toolbar binding now also lives in `src/app_layout_main_toolbar.cpp`, and `App::init_toolbar_main()` is now a thin call-through, but edit-menu wiring and broader layout composition are still inline in `src/app_layout.cpp`. +- Edit-menu binding now also lives in `src/app_layout_edit_menu.cpp`, and + `App::init_menu_edit()` is now a thin call-through, but draw-toolbar, + brush-refresh, and broader layout composition are still inline in + `src/app_layout.cpp`. Write scope: - `src/app_layout.cpp` @@ -476,12 +485,12 @@ Current slice: wakeup posting, stylus frame update, window preference save, and VR lifecycle wrappers now also live in `src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp` -- `main.cpp` startup/window/bootstrap flow now also routes through named local - helpers for runtime-data discovery, startup-state initialization, window - creation, pixel-format setup, GL loader init, runtime-info logging, and - core-context upgrade sequencing, but the retained Win32 shell still needs - those helpers moved into platform-owned modules before the file materially - shrinks +- Win32 startup/window/bootstrap flow now also lives in + `src/platform_windows/windows_bootstrap_helpers.*` for runtime-data + discovery, startup-state initialization, window creation, pixel-format + setup, GL loader init, runtime-info logging, and core-context upgrade + sequencing, which materially thins `src/main.cpp`, but the retained Win32 + window procedure and broader shell flow are 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 diff --git a/src/app_layout.cpp b/src/app_layout.cpp index db293310..7ee654c4 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -32,6 +32,7 @@ namespace pp::panopainter { void bind_legacy_main_toolbar(App& app); void bind_legacy_file_menu(App& app); +void bind_legacy_edit_menu(App& app); void bind_legacy_about_menu(App& app); void bind_legacy_layer_menu(App& app); void bind_legacy_tools_menu(App& app); @@ -54,25 +55,6 @@ bool apply_brush_preset_plan(App& app, const std::shared_ptr& brush) return pp::panopainter::apply_legacy_brush_preset_plan(app, brush); } -std::shared_ptr add_menu_popup( - App& app, - const char* template_id, - glm::vec2 position, - float rtl_anchor_width) -{ - const auto popup = pp::panopainter::add_legacy_popup_menu( - app, - template_id, - position.x, - position.y, - rtl_anchor_width); - if (!popup) { - LOG("Popup menu '%s' failed: %s", template_id ? template_id : "", popup.status().message); - return nullptr; - } - return popup.value(); -} - [[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept { return plan.action == pp::app::ToolsPanelAction::open_floating_panel; @@ -197,15 +179,7 @@ void App::init_menu_file() void App::init_menu_edit() { - if (auto* menu_file = layout[main_id]->find("menu-edit")) - { - menu_file->on_click = [=](Node*) { - glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); - auto popup = add_menu_popup(*this, "edit-menu", pos, menu_file->m_size.x); - if (!popup) - return; - }; - } + pp::panopainter::bind_legacy_edit_menu(*this); } void App::init_menu_tools() diff --git a/src/app_layout_edit_menu.cpp b/src/app_layout_edit_menu.cpp new file mode 100644 index 00000000..4e335b52 --- /dev/null +++ b/src/app_layout_edit_menu.cpp @@ -0,0 +1,47 @@ +#include "pch.h" +#include "app.h" +#include "legacy_ui_overlay_services.h" +#include "node_button_custom.h" +#include "node_popup_menu.h" + +namespace { + +std::shared_ptr add_menu_popup( + App& app, + const char* template_id, + glm::vec2 position, + float rtl_anchor_width) +{ + const auto popup = pp::panopainter::add_legacy_popup_menu( + app, + template_id, + position.x, + position.y, + rtl_anchor_width); + if (!popup) { + LOG("Popup menu '%s' failed: %s", template_id ? template_id : "", popup.status().message); + return nullptr; + } + return popup.value(); +} + +} // namespace + +namespace pp::panopainter { + +void bind_legacy_edit_menu(App& app) +{ + auto main = app.layout[app.main_id]; + + if (auto* menu_edit = main->find("menu-edit")) + { + menu_edit->on_click = [&app, menu_edit](Node*) { + glm::vec2 pos = menu_edit->m_pos + glm::vec2(0, menu_edit->m_size.y); + auto popup = add_menu_popup(app, "edit-menu", pos, menu_edit->m_size.x); + if (!popup) + return; + }; + } +} + +} // namespace pp::panopainter diff --git a/src/legacy_node_stroke_preview_execution_services.h b/src/legacy_node_stroke_preview_execution_services.h index 4484434f..f3a58a86 100644 --- a/src/legacy_node_stroke_preview_execution_services.h +++ b/src/legacy_node_stroke_preview_execution_services.h @@ -10,6 +10,7 @@ #include "legacy_ui_gl_dispatch.h" #include "paint_renderer/compositor.h" #include "texture.h" +#include "util.h" #include #include @@ -225,6 +226,22 @@ inline void bind_legacy_node_stroke_preview_main_pass_textures( } } +inline void copy_legacy_node_stroke_preview_framebuffer_to_texture( + Texture2D& texture, + glm::vec2 size, + std::uint32_t texture_slot) +{ + pp::legacy::ui_gl::activate_texture_unit(texture_slot, "NodeStrokePreview"); + texture.bind(); + copy_framebuffer_to_texture_2d( + 0, + 0, + 0, + 0, + static_cast(size.x), + static_cast(size.y)); +} + template struct LegacyNodeStrokePreviewMainLivePassRequestT { std::function setup_blend_uniforms; @@ -238,6 +255,81 @@ struct LegacyNodeStrokePreviewMainLivePassRequestT { std::function finish_main_pass; }; +inline constexpr std::uint32_t kLegacyNodeStrokePreviewStrokeTextureSlot = 1U; + +template < + typename Frame, + typename ComputeFrames, + typename BeforeFrame, + typename SetupSampleShader, + typename DrawSample, + typename FinishMainPass> +[[nodiscard]] inline LegacyNodeStrokePreviewMainLivePassRequestT +make_legacy_node_stroke_preview_main_live_pass_request( + const Brush& brush, + const LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, + Texture2D& stroke_texture, + RTT& mixer_rtt, + RTT& render_target, + bool copy_stroke_destination, + glm::vec2 size, + std::uint32_t stroke_texture_slot, + ComputeFrames&& compute_frames, + BeforeFrame&& before_frame, + SetupSampleShader&& setup_sample_shader, + DrawSample&& draw_sample, + FinishMainPass&& finish_main_pass) +{ + return LegacyNodeStrokePreviewMainLivePassRequestT { + .setup_blend_uniforms = [&] { + pp::panopainter::apply_legacy_stroke_blend_uniforms( + pass_orchestration.material.stroke_pass.uses_pattern, + brush.m_tip_mix, + brush.m_tip_wet, + brush.m_tip_noise); + }, + .bind_main_pass_textures = [&] { + pp::panopainter::bind_legacy_node_stroke_preview_main_pass_textures( + pp::panopainter::make_legacy_node_stroke_preview_main_pass_texture_dispatch( + [&](int texture_slot) { + pp::legacy::ui_gl::activate_texture_unit(texture_slot, "NodeStrokePreview"); + }, + [&] { + brush.m_tip_texture ? + brush.m_tip_texture->bind() : + pp::legacy::ui_gl::unbind_texture_2d("NodeStrokePreview"); + }, + [&] { + stroke_texture.bind(); + }, + [&] { + brush.m_pattern_texture ? + brush.m_pattern_texture->bind() : + pp::legacy::ui_gl::unbind_texture_2d("NodeStrokePreview"); + }, + [&] { + mixer_rtt.bindTexture(); + }), + copy_stroke_destination, + pass_orchestration.material.stroke_pass.uses_mixer); + }, + .clear_target = [&] { + render_target.clear(); + }, + .compute_frames = std::forward(compute_frames), + .before_frame = std::forward(before_frame), + .setup_sample_shader = std::forward(setup_sample_shader), + .draw_sample = std::forward(draw_sample), + .copy_pass_result = [&] { + copy_legacy_node_stroke_preview_framebuffer_to_texture( + stroke_texture, + size, + stroke_texture_slot); + }, + .finish_main_pass = std::forward(finish_main_pass), + }; +} + template [[nodiscard]] inline bool execute_legacy_node_stroke_preview_main_live_pass( const LegacyNodeStrokePreviewMainLivePassRequestT& request) diff --git a/src/main.cpp b/src/main.cpp index c383c4e0..d14eb47d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,9 +7,7 @@ #include "app.h" #include "canvas.h" #include "keymap.h" -#include "legacy_gl_runtime_dispatch.h" -#include "legacy_preference_storage.h" -#include "renderer_gl/opengl_capabilities.h" +#include "platform_windows/windows_bootstrap_helpers.h" #include "platform_windows/windows_platform_services.h" #include "platform_windows/windows_lifecycle_shell.h" #include "platform_windows/windows_splash.h" @@ -17,22 +15,15 @@ #include "platform_windows/windows_vr_shell.h" #include "../resource.h" -#include #include #include #include "wacom.h" #include "abr.h" -#if __has_include() -#include -#define USE_RENDERDOC -#endif - #include #include #include -LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); namespace pp::platform::windows { void set_async_render_context(HDC hdc, HGLRC hrc); void lock_async_render_context(); @@ -40,20 +31,10 @@ namespace pp::platform::windows { void unlock_async_render_context(); void swap_async_render_context(); } -extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); -extern HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value); -bool win32_renderdoc_init(); -void win32_renderdoc_frame_start(); -void win32_renderdoc_frame_end(); -void init_shcore_API(); -std::string GetLastErrorAsString(); -void _pre_call_callback(const char* name, void* funcptr, int len_args, ...); -void _post_call_callback(const char* name, void* funcptr, int len_args, ...); struct RetainedState { HINSTANCE hInst{}; HWND hWnd{}; - const wchar_t* className{}; bool keys[256]{}; std::map vkey_map; wchar_t window_title[512]{}; @@ -112,239 +93,6 @@ void drain_main_tasks() } } -struct MainWindowStartupState -{ - WNDCLASS window_class{}; - PIXELFORMATDESCRIPTOR pixel_format{}; - DWORD window_style = WS_OVERLAPPEDWINDOW; - RECT client_rect{ 0, 0, 0, 0 }; - POINT client_pos{ CW_USEDEFAULT, CW_USEDEFAULT }; - int show_command = SW_NORMAL; -}; - -struct OpenGlWindowContext -{ - HDC device_context{}; - HGLRC render_context{}; - pp::renderer::gl::OpenGlRuntimeInfo runtime_info{}; -}; - -enum class MainStartupResult -{ - Ok = 0, - GladLoadFailure = 1, - MissingCoreContextSupport = 2, -}; - -void ensure_runtime_data_directory() -{ - FILE* fp_check = fopen("data\\layout.xml", "rb"); - if (!fp_check) - { - LOG("data files not found"); - static char path[MAX_PATH]; - GetModuleFileNameA(NULL, path, MAX_PATH); - LOG("current dir %s", path); - PathRemoveFileSpecA(path); - SetCurrentDirectoryA(path); - LOG("change dir to %s", path); - return; - } - - fclose(fp_check); - LOG("data files ok"); -} - -MainWindowStartupState initialize_main_window_startup_state() -{ - auto& state = retained_state(); - auto startup = MainWindowStartupState {}; - - state.hInst = GetModuleHandle(NULL); - state.className = L"EngineMain"; - - startup.window_class.hInstance = state.hInst; - startup.window_class.lpfnWndProc = (WNDPROC)WndProc; - startup.window_class.lpszClassName = state.className; - startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW; - startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW); - - RegisterClass(&startup.window_class); - - auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY); - auto x = unsigned{ 96 }; - auto y = unsigned{ 96 }; - if (GetDpiForMonitor_fn) - GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y); - App::I->display_density = (float)x / 96.f; - - const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL); - if (window_preferences.has_ui_scale) - App::I->zoom = window_preferences.ui_scale; - else - App::I->zoom = (float)x / 96.f; - - startup.show_command = window_preferences.show_command; - startup.client_rect = { - 0, - 0, - static_cast(App::I->width * App::I->zoom), - static_cast(App::I->height * App::I->zoom), - }; - if (window_preferences.has_window_rect) - { - auto wnd_rect = window_preferences.window_rect; - App::I->width = wnd_rect.z - wnd_rect.x; - App::I->height = wnd_rect.w - wnd_rect.y; - startup.client_rect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w }; - startup.client_pos = { wnd_rect.x, wnd_rect.y }; - } - else - { - AdjustWindowRect(&startup.client_rect, startup.window_style, false); - } - - return startup; -} - -void create_main_window(const MainWindowStartupState& startup, const wchar_t* window_title) -{ - auto& state = retained_state(); - const int window_width = startup.client_rect.right - startup.client_rect.left; - const int window_height = startup.client_rect.bottom - startup.client_rect.top; - state.hWnd = CreateWindow( - startup.window_class.lpszClassName, - window_title, - startup.window_style, - startup.client_pos.x, - startup.client_pos.y, - window_width, - window_height, - 0, - 0, - state.hInst, - 0); -} - -void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format) -{ - pixel_format.nSize = sizeof(pixel_format); - pixel_format.nVersion = 1; - pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pixel_format.iPixelType = PFD_TYPE_RGBA; - pixel_format.cColorBits = 32; - pixel_format.cDepthBits = 24; - pixel_format.iLayerType = PFD_MAIN_PLANE; -} - -bool load_glad_entry_points(HDC device_context) -{ - if (!gladLoadGL()) - { - LOG("gladLoadGL() failed"); - return false; - } - if (!gladLoadWGL(device_context)) - { - LOG("gladLoadWGL() failed"); - return false; - } - - return true; -} - -pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info() -{ - auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {}; - const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info( - pp::legacy::gl_runtime::runtime_info_dispatch()); - if (runtime_info_result.ok()) - { - runtime_info = runtime_info_result.value(); - LOG("GL version: %s", runtime_info.version); - LOG("GL vendor: %s", runtime_info.vendor); - LOG("GL renderer: %s", runtime_info.renderer); - } - else - { - LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message); - } - - return runtime_info; -} - -bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, OpenGlWindowContext& context) -{ - if (!GLAD_WGL_ARB_create_context) - { - LOG("WGL_ARB_create_context not supported"); - // If not supported, go fuck yourself we are not gonna support your shitty device - return false; - } - - const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config(); - UINT num_format = 0; - - wglMakeCurrent(NULL, NULL); - wglDeleteContext(context.render_context); - DestroyWindow(retained_state().hWnd); - - create_main_window(startup, retained_state().window_title); - - context.device_context = GetDC(retained_state().hWnd); - int pixel_format = 0; - wglChoosePixelFormatARB( - context.device_context, - wgl_config.pixel_format_attributes.data(), - nullptr, - 1, - &pixel_format, - &num_format); - SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format); - context.render_context = wglCreateContextAttribsARB( - context.device_context, - NULL, - wgl_config.context_attributes.data()); - wglMakeCurrent(context.device_context, context.render_context); - pp::platform::windows::set_async_render_context(context.device_context, context.render_context); - return true; -} - -MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, OpenGlWindowContext& context) -{ - create_main_window(startup, L"PanoPainter"); - initialize_pixel_format_descriptor(startup.pixel_format); - - context.device_context = GetDC(retained_state().hWnd); - const int pixel_format = ChoosePixelFormat(context.device_context, &startup.pixel_format); - SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format); - context.render_context = wglCreateContext(context.device_context); - wglMakeCurrent(context.device_context, context.render_context); - pp::platform::windows::set_async_render_context(context.device_context, context.render_context); - - if (!load_glad_entry_points(context.device_context)) - return MainStartupResult::GladLoadFailure; - - context.runtime_info = log_runtime_info(); - -#ifdef USE_RENDERDOC - if (!win32_renderdoc_init()) - LOG("Renderdoc not started"); -#endif - - const auto renderer_name = std::string(context.runtime_info.renderer != nullptr ? context.runtime_info.renderer : ""); - swprintf_s( - retained_state().window_title, - L"PanoPainter %s (%s)", - g_version_number_w, - str2wstr(renderer_name).c_str()); - - if (!upgrade_to_core_gl_context(startup, context)) - return MainStartupResult::MissingCoreContextSupport; - - return MainStartupResult::Ok; -} - } HWND pp_windows_main_window_handle() @@ -687,18 +435,19 @@ void win32_save_window_state() int main(int argc, char** argv) { auto& state = retained_state(); + state.hInst = GetModuleHandle(NULL); App::I = new App(); App::I->set_platform_services(&pp::platform::windows::platform_services()); App::I->initLog(); - init_shcore_API(); + pp::platform::windows::init_shcore_API(); pp::platform::windows::initialize_stylus_input(); - if(SetProcessDpiAwareness_fn) - SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE); + if (pp::platform::windows::SetProcessDpiAwareness_fn) + pp::platform::windows::SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE); - ensure_runtime_data_directory(); + pp::platform::windows::ensure_runtime_data_directory(); pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL)); @@ -712,15 +461,15 @@ int main(int argc, char** argv) App::I->create(); memset(&state.keys, 0, sizeof(state.keys)); - auto startup = initialize_main_window_startup_state(); - auto context = OpenGlWindowContext {}; - switch (initialize_main_window_and_gl(startup, context)) + auto startup = pp::platform::windows::initialize_main_window_startup_state(); + auto context = pp::platform::windows::OpenGlWindowContext {}; + switch (pp::platform::windows::initialize_main_window_and_gl(startup, state.hWnd, state.hInst, state.window_title, context)) { - case MainStartupResult::Ok: + case pp::platform::windows::MainStartupResult::Ok: break; - case MainStartupResult::GladLoadFailure: + case pp::platform::windows::MainStartupResult::GladLoadFailure: return 0; - case MainStartupResult::MissingCoreContextSupport: + case pp::platform::windows::MainStartupResult::MissingCoreContextSupport: return -1; } @@ -746,7 +495,7 @@ int main(int argc, char** argv) // link: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registertouchwindow if (RegisterTouchWindow(state.hWnd, 0) == 0) { - LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str()); + LOG("RegisterTouchWindow error: %s", pp::platform::windows::GetLastErrorAsString().c_str()); } wglMakeCurrent(NULL, NULL); @@ -756,8 +505,8 @@ int main(int argc, char** argv) App::I->runtime().ui_thread_start(*App::I); #ifdef _DEBUG - glad_set_pre_callback(_pre_call_callback); - glad_set_post_callback(_post_call_callback); + glad_set_pre_callback(pp::platform::windows::_pre_call_callback); + glad_set_post_callback(pp::platform::windows::_post_call_callback); #endif if (!state.sandboxed) diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index f7ef6abc..d5132de6 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -199,22 +199,6 @@ pp::panopainter::LegacyCanvasStrokeMixPassRequest make_stroke_preview_mix_pass_e }); } -void copy_stroke_preview_framebuffer_to_texture( - Texture2D& texture, - glm::vec2 size, - std::uint32_t texture_unit) -{ - set_active_texture_unit(texture_unit); - texture.bind(); - copy_framebuffer_to_texture_2d( - 0, - 0, - 0, - 0, - static_cast(size.x), - static_cast(size.y)); -} - void bind_stroke_preview_live_samplers( Sampler& mipmap_sampler, Sampler& linear_sampler, @@ -235,38 +219,6 @@ void bind_stroke_preview_dual_pass_textures(const Brush& dual_brush) unbind_texture_2d(); } -void bind_stroke_preview_main_pass_textures( - const Brush& brush, - Texture2D& stroke_destination_texture, - RTT& mixer_rtt, - bool copy_stroke_destination, - bool uses_mixer) -{ - pp::panopainter::bind_legacy_node_stroke_preview_main_pass_textures( - pp::panopainter::make_legacy_node_stroke_preview_main_pass_texture_dispatch( - [&](int texture_slot) { - set_active_texture_unit(texture_slot); - }, - [&] { - brush.m_tip_texture ? - brush.m_tip_texture->bind() : - unbind_texture_2d(); - }, - [&] { - stroke_destination_texture.bind(); - }, - [&] { - brush.m_pattern_texture ? - brush.m_pattern_texture->bind() : - unbind_texture_2d(); - }, - [&] { - mixer_rtt.bindTexture(); - }), - copy_stroke_destination, - uses_mixer); -} - void bind_stroke_preview_destination_texture(Texture2D& texture) { set_active_texture_unit(stroke_preview_live_slots::kDestination); @@ -782,29 +734,19 @@ NodeStrokePreview::make_stroke_draw_immediate_main_live_pass_request( float zoom, const glm::vec2& size) { - return StrokeMainLivePassRequest { - .setup_blend_uniforms = [&] { - pp::panopainter::apply_legacy_stroke_blend_uniforms( - pass_orchestration.material.stroke_pass.uses_pattern, - brush.m_tip_mix, - brush.m_tip_wet, - brush.m_tip_noise); - }, - .bind_main_pass_textures = [&] { - bind_stroke_preview_main_pass_textures( - brush, - m_tex, - m_rtt_mixer, - copy_stroke_destination, - pass_orchestration.material.stroke_pass.uses_mixer); - }, - .clear_target = [&] { - m_rtt.clear(); - }, - .compute_frames = [&] { + return pp::panopainter::make_legacy_node_stroke_preview_main_live_pass_request( + brush, + pass_orchestration, + m_tex, + m_rtt_mixer, + m_rtt, + copy_stroke_destination, + size, + pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot, + [&] { return stroke_draw_compute(stroke, zoom); }, - .before_frame = [&](auto& frame) { + [&](auto& frame) { if (brush.m_tip_mix > 0.f) { stroke_draw_mix(xy(frame.m_mixer_rect), zw(frame.m_mixer_rect)); @@ -815,24 +757,17 @@ NodeStrokePreview::make_stroke_draw_immediate_main_live_pass_request( glm::vec4 { 0, 0, 0, 1 }; frame.flow = glm::max(frame.flow, m_min_flow); }, - .setup_sample_shader = [&](auto& frame) { + [&](auto& frame) { execute_stroke_draw_immediate_main_live_sample_pass( brush, copy_stroke_destination, frame, size); }, - .draw_sample = [&](auto& frame) { + [&](auto& frame) { /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination); }, - .copy_pass_result = [&] { - copy_stroke_preview_framebuffer_to_texture( - m_tex, - size, - stroke_preview_composite_slots::kStroke); - }, - .finish_main_pass = [&] { set_active_texture_unit(stroke_preview_live_slots::kMixer); m_rtt_mixer.unbindTexture(); }, - }; + [&] { set_active_texture_unit(stroke_preview_live_slots::kMixer); m_rtt_mixer.unbindTexture(); }); } void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass( @@ -869,7 +804,7 @@ void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass( /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex_dual, copy_stroke_destination); }, [&] { - copy_stroke_preview_framebuffer_to_texture( + pp::panopainter::copy_legacy_node_stroke_preview_framebuffer_to_texture( m_tex_dual, size, stroke_preview_composite_slots::kStroke); diff --git a/src/platform_windows/windows_bootstrap_helpers.cpp b/src/platform_windows/windows_bootstrap_helpers.cpp index 4bcb43bf..1a69b897 100644 --- a/src/platform_windows/windows_bootstrap_helpers.cpp +++ b/src/platform_windows/windows_bootstrap_helpers.cpp @@ -1,7 +1,11 @@ #include "pch.h" + +#include "platform_windows/windows_bootstrap_helpers.h" + #include "app.h" +#include "legacy_gl_runtime_dispatch.h" +#include "legacy_preference_storage.h" #include "log.h" -#include "renderer_gl/opengl_capabilities.h" #include #include @@ -11,6 +15,14 @@ #define USE_RENDERDOC #endif +LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); + +namespace pp::platform::windows { +void set_async_render_context(HDC hdc, HGLRC hrc); +} + +namespace pp::platform::windows { + #ifdef USE_RENDERDOC RENDERDOC_API_1_4_0* rdoc_api = NULL; @@ -75,6 +87,213 @@ std::string GetLastErrorAsString() return message; } +void ensure_runtime_data_directory() +{ + FILE* fp_check = fopen("data\\layout.xml", "rb"); + if (!fp_check) + { + LOG("data files not found"); + static char path[MAX_PATH]; + GetModuleFileNameA(NULL, path, MAX_PATH); + LOG("current dir %s", path); + PathRemoveFileSpecA(path); + SetCurrentDirectoryA(path); + LOG("change dir to %s", path); + return; + } + + fclose(fp_check); + LOG("data files ok"); +} + +MainWindowStartupState initialize_main_window_startup_state() +{ + auto startup = MainWindowStartupState {}; + const auto hInst = GetModuleHandle(NULL); + const auto className = L"EngineMain"; + + startup.window_class.hInstance = hInst; + startup.window_class.lpfnWndProc = (WNDPROC)WndProc; + startup.window_class.lpszClassName = className; + startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW; + startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW); + + RegisterClass(&startup.window_class); + + auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY); + auto x = unsigned{ 96 }; + auto y = unsigned{ 96 }; + if (GetDpiForMonitor_fn) + GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y); + App::I->display_density = (float)x / 96.f; + + const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL); + if (window_preferences.has_ui_scale) + App::I->zoom = window_preferences.ui_scale; + else + App::I->zoom = (float)x / 96.f; + + startup.show_command = window_preferences.show_command; + startup.client_rect = { + 0, + 0, + static_cast(App::I->width * App::I->zoom), + static_cast(App::I->height * App::I->zoom), + }; + if (window_preferences.has_window_rect) + { + auto wnd_rect = window_preferences.window_rect; + App::I->width = wnd_rect.z - wnd_rect.x; + App::I->height = wnd_rect.w - wnd_rect.y; + startup.client_rect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w }; + startup.client_pos = { wnd_rect.x, wnd_rect.y }; + } + else + { + AdjustWindowRect(&startup.client_rect, startup.window_style, false); + } + + return startup; +} + +void create_main_window(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title) +{ + const int window_width = startup.client_rect.right - startup.client_rect.left; + const int window_height = startup.client_rect.bottom - startup.client_rect.top; + hWnd = CreateWindow( + startup.window_class.lpszClassName, + window_title, + startup.window_style, + startup.client_pos.x, + startup.client_pos.y, + window_width, + window_height, + 0, + 0, + hInst, + 0); +} + +void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format) +{ + pixel_format.nSize = sizeof(pixel_format); + pixel_format.nVersion = 1; + pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pixel_format.iPixelType = PFD_TYPE_RGBA; + pixel_format.cColorBits = 32; + pixel_format.cDepthBits = 24; + pixel_format.iLayerType = PFD_MAIN_PLANE; +} + +bool load_glad_entry_points(HDC device_context) +{ + if (!gladLoadGL()) + { + LOG("gladLoadGL() failed"); + return false; + } + if (!gladLoadWGL(device_context)) + { + LOG("gladLoadWGL() failed"); + return false; + } + + return true; +} + +pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info() +{ + auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {}; + const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info( + pp::legacy::gl_runtime::runtime_info_dispatch()); + if (runtime_info_result.ok()) + { + runtime_info = runtime_info_result.value(); + LOG("GL version: %s", runtime_info.version); + LOG("GL vendor: %s", runtime_info.vendor); + LOG("GL renderer: %s", runtime_info.renderer); + } + else + { + LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message); + } + + return runtime_info; +} + +bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title, OpenGlWindowContext& context) +{ + if (!GLAD_WGL_ARB_create_context) + { + LOG("WGL_ARB_create_context not supported"); + // If not supported, go fuck yourself we are not gonna support your shitty device + return false; + } + + const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config(); + UINT num_format = 0; + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(context.render_context); + DestroyWindow(hWnd); + + create_main_window(startup, hWnd, hInst, window_title); + + context.device_context = GetDC(hWnd); + int pixel_format = 0; + wglChoosePixelFormatARB( + context.device_context, + wgl_config.pixel_format_attributes.data(), + nullptr, + 1, + &pixel_format, + &num_format); + SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format); + context.render_context = wglCreateContextAttribsARB( + context.device_context, + NULL, + wgl_config.context_attributes.data()); + wglMakeCurrent(context.device_context, context.render_context); + set_async_render_context(context.device_context, context.render_context); + return true; +} + +MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, wchar_t* window_title, OpenGlWindowContext& context) +{ + create_main_window(startup, hWnd, hInst, L"PanoPainter"); + initialize_pixel_format_descriptor(startup.pixel_format); + + context.device_context = GetDC(hWnd); + const int pixel_format = ChoosePixelFormat(context.device_context, &startup.pixel_format); + SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format); + context.render_context = wglCreateContext(context.device_context); + wglMakeCurrent(context.device_context, context.render_context); + set_async_render_context(context.device_context, context.render_context); + + if (!load_glad_entry_points(context.device_context)) + return MainStartupResult::GladLoadFailure; + + context.runtime_info = log_runtime_info(); + +#ifdef USE_RENDERDOC + if (!win32_renderdoc_init()) + LOG("Renderdoc not started"); +#endif + + const auto renderer_name = std::string(context.runtime_info.renderer != nullptr ? context.runtime_info.renderer : ""); + swprintf_s( + window_title, + 512, + L"PanoPainter %s (%s)", + g_version_number_w, + str2wstr(renderer_name).c_str()); + + if (!upgrade_to_core_gl_context(startup, hWnd, hInst, window_title, context)) + return MainStartupResult::MissingCoreContextSupport; + + return MainStartupResult::Ok; +} + BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) { RECT rc; @@ -104,3 +323,5 @@ void _post_call_callback(const char* name, void* funcptr, int len_args, ...) LOG("ERROR %d in %s\n", error_code, name); } } + +} diff --git a/src/platform_windows/windows_bootstrap_helpers.h b/src/platform_windows/windows_bootstrap_helpers.h new file mode 100644 index 00000000..0c9e1870 --- /dev/null +++ b/src/platform_windows/windows_bootstrap_helpers.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include + +#include "renderer_gl/opengl_capabilities.h" + +namespace pp::platform::windows { + +bool win32_renderdoc_init(); +void win32_renderdoc_frame_start(); +void win32_renderdoc_frame_end(); + +extern HRESULT (*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); +extern HRESULT (*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value); + +void init_shcore_API(); +std::string GetLastErrorAsString(); +void _pre_call_callback(const char* name, void* funcptr, int len_args, ...); +void _post_call_callback(const char* name, void* funcptr, int len_args, ...); + +struct MainWindowStartupState +{ + WNDCLASS window_class{}; + PIXELFORMATDESCRIPTOR pixel_format{}; + DWORD window_style = WS_OVERLAPPEDWINDOW; + RECT client_rect{ 0, 0, 0, 0 }; + POINT client_pos{ CW_USEDEFAULT, CW_USEDEFAULT }; + int show_command = SW_NORMAL; +}; + +struct OpenGlWindowContext +{ + HDC device_context{}; + HGLRC render_context{}; + pp::renderer::gl::OpenGlRuntimeInfo runtime_info{}; +}; + +enum class MainStartupResult +{ + Ok = 0, + GladLoadFailure = 1, + MissingCoreContextSupport = 2, +}; + +void ensure_runtime_data_directory(); +MainWindowStartupState initialize_main_window_startup_state(); +void create_main_window(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title); +void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format); +bool load_glad_entry_points(HDC device_context); +pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info(); +bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title, OpenGlWindowContext& context); +MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, wchar_t* window_title, OpenGlWindowContext& context); + +} diff --git a/src/platform_windows/windows_platform_services.cpp b/src/platform_windows/windows_platform_services.cpp index 5c44036c..b278f98d 100644 --- a/src/platform_windows/windows_platform_services.cpp +++ b/src/platform_windows/windows_platform_services.cpp @@ -1,4 +1,5 @@ #include "pch.h" +#include "platform_windows/windows_bootstrap_helpers.h" #include "platform_windows/windows_platform_services.h" #include "log.h" @@ -15,19 +16,15 @@ void destroy_window(); void async_lock(); void async_unlock(); void win32_async_swap(); -void win32_renderdoc_frame_start(); -void win32_renderdoc_frame_end(); void win32_update_fps(int frames); void win32_update_stylus(float dt); void win32_save_window_state(); bool win32_vr_start(); void win32_vr_stop(); -std::string GetLastErrorAsString(); HWND pp_windows_main_window_handle(); void pp_windows_enqueue_main_task(std::packaged_task task); namespace pp::platform::windows { - namespace { struct RetainedWin32AsyncRenderContextState final { @@ -43,8 +40,7 @@ struct RetainedWin32AsyncRenderContextState final { static RetainedWin32AsyncRenderContextState state; return state; } - -} +} // namespace void set_async_render_context(HDC hdc, HGLRC hrc) { @@ -63,7 +59,7 @@ void lock_async_render_context() state.gl_mutex.lock(); const bool ret = wglMakeCurrent(state.hdc, state.hrc); if (!ret) - LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str()); + LOG("FAILED wglMakeCurrent: %s", pp::platform::windows::GetLastErrorAsString().c_str()); state.gl_thread = std::this_thread::get_id(); } state.gl_count++; @@ -78,7 +74,7 @@ bool try_lock_async_render_context() return false; const bool ret = wglMakeCurrent(state.hdc, state.hrc); if (!ret) - LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str()); + LOG("FAILED wglMakeCurrent: %s", pp::platform::windows::GetLastErrorAsString().c_str()); state.gl_thread = std::this_thread::get_id(); } state.gl_count++; @@ -100,8 +96,7 @@ void swap_async_render_context() { SwapBuffers(retained_win32_async_render_context_state().hdc); } - -} +} // namespace pp::platform::windows namespace { @@ -438,12 +433,12 @@ public: void begin_render_capture_frame() override { - win32_renderdoc_frame_start(); + pp::platform::windows::win32_renderdoc_frame_start(); } void end_render_capture_frame() override { - win32_renderdoc_frame_end(); + pp::platform::windows::win32_renderdoc_frame_end(); } [[nodiscard]] bool deletes_recorded_files_on_clear() override diff --git a/src/platform_windows/windows_splash.cpp b/src/platform_windows/windows_splash.cpp index 3db396de..227b6e84 100644 --- a/src/platform_windows/windows_splash.cpp +++ b/src/platform_windows/windows_splash.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "platform_windows/windows_splash.h" +#include "platform_windows/windows_bootstrap_helpers.h" #include "app.h" #include "image.h" @@ -13,8 +14,6 @@ #include #include -extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); - namespace { constexpr UINT kSplashCloseMessage = WM_USER + 1; @@ -42,8 +41,8 @@ LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lPara auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY); auto x = unsigned{ 96 }; auto y = unsigned{ 96 }; - if (GetDpiForMonitor_fn) - GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y); + if (pp::platform::windows::GetDpiForMonitor_fn) + pp::platform::windows::GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y); float z = (float)x / 96.f; static char base_path[MAX_PATH];