diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 906f6760..009f36c6 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -97,6 +97,7 @@ set(PP_PANOPAINTER_APP_SOURCES src/app_dialogs_info_openers.cpp src/app_events.cpp src/app_layout.cpp + src/app_layout_brush.cpp src/app_layout_draw_toolbar.cpp src/app_layout_ui_state.cpp src/app_layout_sidebar.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 17e83acf..bae964b4 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -80,13 +80,13 @@ What is still carrying too much live ownership: Current hotspot files: - `src/canvas.cpp`: 2645 lines -- `src/app_layout.cpp`: 285 lines +- `src/app_layout.cpp`: 229 lines - `src/canvas_modes.cpp`: 1798 lines - `src/node.cpp`: 1594 lines -- `src/main.cpp`: 434 lines +- `src/main.cpp`: 390 lines - `src/node_panel_brush.cpp`: 1197 lines - `src/node_stroke_preview.cpp`: 890 lines -- `src/node_canvas.cpp`: 881 lines +- `src/node_canvas.cpp`: 831 lines - `src/app.cpp`: 502 lines - `src/app_dialogs.cpp`: 142 lines @@ -132,7 +132,10 @@ Current architecture mismatches that must be treated as real blockers: routes through `execute_node_canvas_draw_merge_tail(...)`, while the unmerged-path onion-range planning, plane filtering, per-layer visit handling, and per-visit layer-path execution now also route through - `execute_legacy_canvas_draw_unmerged_node_canvas_shell(...)`. + `execute_legacy_canvas_draw_unmerged_node_canvas_shell(...)`, while the + broader unmerged cache/viewport/background/composite pass setup now also + routes through + `execute_legacy_canvas_draw_unmerged_node_canvas_pass(...)`. - `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 @@ -150,8 +153,9 @@ Current architecture mismatches that must be treated as real blockers: `App::init_menu_edit()` is now a thin call-through, while UI-direction and persisted floating/docked panel-state ownership now also live in `src/app_layout_ui_state.cpp`, while draw-toolbar binding now also lives in - `src/app_layout_draw_toolbar.cpp`, and `src/app_layout.cpp` is now mostly - brush-refresh and layout/bootstrap composition, while the + `src/app_layout_draw_toolbar.cpp`, while brush-refresh now also lives in + `src/app_layout_brush.cpp`, and `src/app_layout.cpp` is now mostly + layout/bootstrap composition, 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 @@ -175,9 +179,9 @@ Current architecture mismatches that must be treated as real blockers: retained local state object instead of separate process-wide globals, the Win32 async GL/context lock state now lives under `src/platform_windows/windows_platform_services.cpp` instead of `main.cpp` - retained state, the main-thread queued task state now sits behind a narrow - retained helper instead of `RetainedState.main_tasklist` / - `main_task_mutex`, the canvas async worker now sits behind a named retained + retained state, the main-thread queued task state now lives under + `src/platform_windows/windows_platform_services.cpp` instead of staying in + `src/main.cpp`, the canvas async worker now sits behind a named retained local worker-state helper instead of a bare static accessor, the prepared-file worker and the canvas async import/export/save/open worker now live under `AppRuntime` instead of retained static app-events/canvas diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 9965045e..c36875ee 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -230,6 +230,11 @@ Current slice: `execute_legacy_canvas_draw_unmerged_node_canvas_shell(...)`, which trims another outer draw-shell block even though the broader node draw loop still lives in `src/node_canvas.cpp`. +- `NodeCanvas` broader unmerged cache/viewport/background/composite pass setup + now also routes through + `execute_legacy_canvas_draw_unmerged_node_canvas_pass(...)`, which removes + another coherent outer-shell block even though the broader node draw loop + still lives in `src/node_canvas.cpp`. Write scope: - `src/node_stroke_preview.cpp` @@ -308,7 +313,7 @@ targets look like helpers under one old monolith. Status: In Progress Why now: -`src/app_layout.cpp` is still a 285-line mixed file that builds menus, +`src/app_layout.cpp` is still a 229-line mixed file that builds menus, attaches callbacks, computes planner inputs, and mutates UI state directly. Current slice: @@ -341,6 +346,8 @@ Current slice: - Draw-toolbar binding now also lives in `src/app_layout_draw_toolbar.cpp`, and `src/app_layout.cpp` is down to the remaining brush-refresh and layout/bootstrap composition. +- Brush-refresh now also lives in `src/app_layout_brush.cpp`, and + `src/app_layout.cpp` is down to the remaining layout/bootstrap composition. Write scope: - `src/app_layout.cpp` @@ -483,8 +490,9 @@ Current slice: `src/platform_windows/windows_platform_services.cpp` instead of `main.cpp` retained state, and `main.cpp` only seeds that platform-owned context handle pair during initialization and context recreation -- `main.cpp` main-thread queued task state now sits behind a narrow retained - helper instead of `RetainedState.main_tasklist` / `main_task_mutex` directly +- `main.cpp` main-thread queued task state now lives under + `src/platform_windows/windows_platform_services.cpp` instead of staying in + the entry TU - Win32 pointer API loading, stylus/ink timer ownership and decay, `WT_PACKET` reset, and `WM_POINTERUPDATE` pen/touch handling now live in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`, diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 606b3664..b3b3e45a 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -9,7 +9,6 @@ #include "node_panel_floating.h" #include "app_core/about_menu.h" #include "app_core/app_preferences.h" -#include "app_core/brush_ui.h" #include "app_core/document_layer.h" #include "app_core/document_canvas.h" #include "app_core/app_status.h" @@ -114,64 +113,6 @@ void App::init_menu_about() pp::panopainter::bind_legacy_about_menu(*this); } -void App::brush_update(bool update_color, bool update_brush) -{ -// brushes->select_brush(canvas->m_brush->id); -// stroke->set_params(canvas->m_brush); - render_task_async([this, update_color, update_brush] - { - pp::app::BrushUiRefreshInput input; - input.update_color = update_color; - input.update_brush = update_brush; - auto current_brush = Canvas::I ? Canvas::I->m_current_brush : nullptr; - input.has_current_brush = current_brush != nullptr; - input.has_floating_picker = floating_picker != nullptr; - input.has_floating_color_panel = floating_color != nullptr; - if (input.has_current_brush) - { - input.tip_flow = current_brush->m_tip_flow; - input.tip_size = current_brush->m_tip_size; - input.r = current_brush->m_tip_color.r; - input.g = current_brush->m_tip_color.g; - input.b = current_brush->m_tip_color.b; - input.a = current_brush->m_tip_color.a; - } - - const auto view = pp::app::plan_brush_ui_refresh(input); - if (!view) { - LOG("Brush UI refresh failed: %s", view.status().message); - return; - } - - if (view.value().updates_stroke_controls) - { - stroke->update_controls(); - } - if (view.value().updates_quick_flow) - { - quick->m_slider_flow->set_value(stroke->m_tip_flow->get_value()); - } - if (view.value().updates_quick_size) - { - quick->m_slider_size->set_value(stroke->m_tip_size->get_value()); - } - if (view.value().updates_quick_brush_preview && current_brush) - { - *quick->m_button_brush_current_preview->m_brush = *current_brush; - quick->m_button_brush_current_preview->draw_stroke(); - } - if (view.value().updates_quick_color) - { - const glm::vec4 color(view.value().r, view.value().g, view.value().b, view.value().a); - quick->m_button_color_current_inner->m_color = color; - if (view.value().updates_floating_picker) - floating_picker->set_color(color); - if (view.value().updates_floating_color_panel) - floating_color->set_color(color); - } - }, true); -} - void App::init_menu_layer() { pp::panopainter::bind_legacy_layer_menu(*this); diff --git a/src/app_layout_brush.cpp b/src/app_layout_brush.cpp new file mode 100644 index 00000000..746e5e5f --- /dev/null +++ b/src/app_layout_brush.cpp @@ -0,0 +1,61 @@ +#include "pch.h" +#include "app.h" +#include "app_core/brush_ui.h" + +void App::brush_update(bool update_color, bool update_brush) +{ +// brushes->select_brush(canvas->m_brush->id); +// stroke->set_params(canvas->m_brush); + render_task_async([this, update_color, update_brush] + { + pp::app::BrushUiRefreshInput input; + input.update_color = update_color; + input.update_brush = update_brush; + auto current_brush = Canvas::I ? Canvas::I->m_current_brush : nullptr; + input.has_current_brush = current_brush != nullptr; + input.has_floating_picker = floating_picker != nullptr; + input.has_floating_color_panel = floating_color != nullptr; + if (input.has_current_brush) + { + input.tip_flow = current_brush->m_tip_flow; + input.tip_size = current_brush->m_tip_size; + input.r = current_brush->m_tip_color.r; + input.g = current_brush->m_tip_color.g; + input.b = current_brush->m_tip_color.b; + input.a = current_brush->m_tip_color.a; + } + + const auto view = pp::app::plan_brush_ui_refresh(input); + if (!view) { + LOG("Brush UI refresh failed: %s", view.status().message); + return; + } + + if (view.value().updates_stroke_controls) + { + stroke->update_controls(); + } + if (view.value().updates_quick_flow) + { + quick->m_slider_flow->set_value(stroke->m_tip_flow->get_value()); + } + if (view.value().updates_quick_size) + { + quick->m_slider_size->set_value(stroke->m_tip_size->get_value()); + } + if (view.value().updates_quick_brush_preview && current_brush) + { + *quick->m_button_brush_current_preview->m_brush = *current_brush; + quick->m_button_brush_current_preview->draw_stroke(); + } + if (view.value().updates_quick_color) + { + const glm::vec4 color(view.value().r, view.value().g, view.value().b, view.value().a); + quick->m_button_color_current_inner->m_color = color; + if (view.value().updates_floating_picker) + floating_picker->set_color(color); + if (view.value().updates_floating_color_panel) + floating_color->set_color(color); + } + }, true); +} diff --git a/src/legacy_canvas_draw_merge_services.h b/src/legacy_canvas_draw_merge_services.h index 4c3506b9..39a6a412 100644 --- a/src/legacy_canvas_draw_merge_services.h +++ b/src/legacy_canvas_draw_merge_services.h @@ -1112,6 +1112,113 @@ inline void execute_legacy_canvas_draw_unmerged_node_canvas_shell( } } +template < + typename NodeCanvasT, + typename ApplyViewport, + typename ApplyCapability, + typename DrawFacePlane, + typename BindSampler, + typename MakeLayerPathExecution, + typename MakeCacheToScreenCheckerboardPlane, + typename LogOnionRangeFailure> +inline void execute_legacy_canvas_draw_unmerged_node_canvas_pass( + NodeCanvasT& node_canvas, + bool use_blend, + const glm::mat4& proj, + const glm::mat4& camera, + const glm::mat4& orientation, + const glm::ivec4& c, + ApplyViewport&& apply_node_canvas_viewport, + ApplyCapability&& apply_node_canvas_capability, + DrawFacePlane&& draw_face_plane, + BindSampler&& bind_sampler, + MakeLayerPathExecution&& make_layer_path_execution, + MakeCacheToScreenCheckerboardPlane&& make_cache_to_screen_checkerboard_plane, + LogOnionRangeFailure&& log_onion_range_failure) +{ + const auto prepare_blend_cache = [&] { + apply_node_canvas_viewport(0, 0, node_canvas.m_cache_rtt.getWidth(), node_canvas.m_cache_rtt.getHeight()); + node_canvas.m_cache_rtt.bindFramebuffer(); + node_canvas.m_cache_rtt.clear({ 1, 1, 1, 0 }); + }; + + const auto draw_background = [&] { + execute_legacy_canvas_draw_merge_background_setup( + { + .use_blend = use_blend, + }, + { + .disable_blend = [&] { + apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); + }, + .draw_checkerboard_plane = make_legacy_canvas_draw_merge_background_checkerboard_plane( + proj, + camera, + node_canvas.m_canvas->m_layers.size() + 500.f, + node_canvas.m_canvas->m_plane_transform, + draw_face_plane), + }); + }; + + const auto configure_blend_state = [&](bool enable_shader_blend) { + apply_node_canvas_capability(pp::renderer::gl::blend_state(), !enable_shader_blend); + }; + + const auto disable_depth_test = [&] { + apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); + }; + + execute_legacy_canvas_draw_unmerged_node_canvas_shell( + node_canvas, + use_blend, + proj, + camera, + orientation, + prepare_blend_cache, + draw_background, + configure_blend_state, + disable_depth_test, + make_layer_path_execution, + log_onion_range_failure, + [&] { + node_canvas.m_cache_rtt.unbindFramebuffer(); + if (node_canvas.m_density != 1.f) { + apply_node_canvas_viewport(0, 0, node_canvas.m_rtt.getWidth(), node_canvas.m_rtt.getHeight()); + } else { + apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); + } + + execute_legacy_canvas_draw_merge_cache_to_screen_composite( + LegacyCanvasDrawMergeCacheToScreenCompositeUniforms { + .checkerboard = { + .colorize = false, + }, + .texture = { + .mvp = glm::ortho(-1, 1, -1, 1), + .texture_slot = 0, + }, + }, + { + .enable_blend = [&] { + apply_node_canvas_capability(pp::renderer::gl::blend_state(), true); + }, + .draw_checkerboard_plane = make_cache_to_screen_checkerboard_plane(), + .bind_sampler = [&] { + bind_sampler(); + }, + .bind_cache_texture = [&] { + node_canvas.m_cache_rtt.bindTexture(); + }, + .draw_cache_texture = [&] { + draw_face_plane(); + }, + .unbind_cache_texture = [&] { + node_canvas.m_cache_rtt.unbindTexture(); + }, + }); + }); +} + inline void execute_legacy_canvas_draw_merge_plane_setup( const LegacyCanvasDrawMergePlaneSetupUniforms& uniforms, const LegacyCanvasDrawMergePlaneSetupExecution& execution) diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 26d4ca7f..cde6138d 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -670,42 +670,25 @@ void NodeCanvas::draw() const auto layer_orientation = glm::eulerAngleYXZ(yaw, pitch, roll); const auto& b = m_canvas->m_current_stroke->m_brush; - pp::panopainter::execute_legacy_canvas_draw_unmerged_node_canvas_shell( + pp::panopainter::execute_legacy_canvas_draw_unmerged_node_canvas_pass( *this, use_blend, proj, camera, layer_orientation, - [&] { - apply_node_canvas_viewport(0, 0, m_cache_rtt.getWidth(), m_cache_rtt.getHeight()); - m_cache_rtt.bindFramebuffer(); - m_cache_rtt.clear({ 1, 1, 1, 0 }); + c, + [&](int x, int y, int width, int height) { + apply_node_canvas_viewport(x, y, width, height); + }, + [&](auto state, bool enabled) { + apply_node_canvas_capability(state, enabled); }, [&] { - pp::panopainter::execute_legacy_canvas_draw_merge_background_setup( - { - .use_blend = use_blend, - }, - { - .disable_blend = [&] { - apply_node_canvas_capability(pp::renderer::gl::blend_state(), false); - }, - .draw_checkerboard_plane = pp::panopainter::make_legacy_canvas_draw_merge_background_checkerboard_plane( - proj, - camera, - m_canvas->m_layers.size() + 500.f, - m_canvas->m_plane_transform, - [&] { - m_face_plane.draw_fill(); - }), - }); - }, - [&](bool enable_shader_blend) { - // if not using shader blend, use gl rasterizer blend - apply_node_canvas_capability(pp::renderer::gl::blend_state(), !enable_shader_blend); + m_face_plane.draw_fill(); }, [&] { - apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); + m_sampler.bind(0); + set_active_texture_unit(0); }, [&](size_t layer_index, int plane_index, const glm::mat4& plane_mvp_z) { return make_node_canvas_layer_path_execution( @@ -717,51 +700,18 @@ void NodeCanvas::draw() copy_blend_destination, m_canvas->m_cam_fov < 20.f); }, + [&] { + return pp::panopainter::make_legacy_canvas_draw_merge_cache_to_screen_checkerboard_plane( + proj, + camera, + m_canvas->m_layers.size() + 500.f, + m_canvas->m_plane_transform, + [&] { + m_face_plane.draw_fill(); + }); + }, [&](const char* message) { LOG("NodeCanvas onion frame range failed: %s", message); - }, - [&] { - m_cache_rtt.unbindFramebuffer(); - if (m_density != 1.f) - apply_node_canvas_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); - else - apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); - pp::panopainter::execute_legacy_canvas_draw_merge_cache_to_screen_composite( - pp::panopainter::LegacyCanvasDrawMergeCacheToScreenCompositeUniforms { - .checkerboard = { - .colorize = false, - }, - .texture = { - .mvp = glm::ortho(-1, 1, -1, 1), - .texture_slot = 0, - }, - }, - { - .enable_blend = [&] { - apply_node_canvas_capability(pp::renderer::gl::blend_state(), true); - }, - .draw_checkerboard_plane = pp::panopainter::make_legacy_canvas_draw_merge_cache_to_screen_checkerboard_plane( - proj, - camera, - m_canvas->m_layers.size() + 500.f, - m_canvas->m_plane_transform, - [&] { - m_face_plane.draw_fill(); - }), - .bind_sampler = [&] { - m_sampler.bind(0); - set_active_texture_unit(0); - }, - .bind_cache_texture = [&] { - m_cache_rtt.bindTexture(); - }, - .draw_cache_texture = [&] { - m_face_plane.draw_fill(); - }, - .unbind_cache_texture = [&] { - m_cache_rtt.unbindTexture(); - }, - }); }); }