From c25af6f493af706ea9811d7eb7c3d0c123662f90 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 16 Jun 2026 20:36:47 +0200 Subject: [PATCH] Extract app runtime shell, canvas render shell, and node tree services --- cmake/PanoPainterSources.cmake | 4 + docs/modernization/roadmap.md | 23 +- docs/modernization/tasks.md | 24 +- src/app.cpp | 237 ------- src/canvas.cpp | 657 +----------------- src/legacy_app_runtime_shell_services.cpp | 239 +++++++ src/legacy_canvas_render_shell_services.cpp | 727 ++++++++++++++++++++ src/legacy_canvas_render_shell_services.h | 40 ++ src/legacy_ui_node_tree_services.cpp | 377 ++++++++++ src/node.cpp | 373 ---------- 10 files changed, 1450 insertions(+), 1251 deletions(-) create mode 100644 src/legacy_app_runtime_shell_services.cpp create mode 100644 src/legacy_canvas_render_shell_services.cpp create mode 100644 src/legacy_canvas_render_shell_services.h create mode 100644 src/legacy_ui_node_tree_services.cpp diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index a2554831..143a1363 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -20,6 +20,8 @@ set(PP_LEGACY_PAINT_DOCUMENT_SOURCES src/bezier.cpp src/brush.cpp src/canvas.cpp + src/legacy_canvas_render_shell_services.cpp + src/legacy_canvas_render_shell_services.h src/canvas_actions.cpp src/canvas_layer.cpp src/legacy_canvas_layer_services.cpp @@ -52,6 +54,7 @@ set(PP_LEGACY_UI_CORE_SOURCES src/legacy_ui_node_event.cpp src/legacy_ui_node_event.h src/legacy_ui_node_execution.cpp + src/legacy_ui_node_tree_services.cpp src/layout.cpp src/node.cpp src/node_border.cpp @@ -129,6 +132,7 @@ set(PP_PANOPAINTER_APP_SOURCES src/app_layout_tools_menu.cpp src/app_shaders.cpp src/app_vr.cpp + src/legacy_app_runtime_shell_services.cpp src/legacy_app_frame_services.cpp src/legacy_app_dialog_services.cpp src/legacy_app_dialog_services.h diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index d257bc3b..b5ad0461 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -79,15 +79,15 @@ What is still carrying too much live ownership: Current hotspot files: -- `src/canvas.cpp`: 1010 lines +- `src/canvas.cpp`: 429 lines - `src/app_layout.cpp`: 125 lines - `src/canvas_modes.cpp`: 720 lines -- `src/node.cpp`: 803 lines +- `src/node.cpp`: 465 lines - `src/main.cpp`: 271 lines - `src/node_panel_brush.cpp`: 435 lines - `src/node_stroke_preview.cpp`: 607 lines - `src/node_canvas.cpp`: 219 lines -- `src/app.cpp`: 502 lines +- `src/app.cpp`: 292 lines - `src/app_dialogs.cpp`: 168 lines Current architecture mismatches that must be treated as real blockers: @@ -332,7 +332,22 @@ Current architecture mismatches that must be treated as real blockers: while `Canvas` point-trace/unproject/project/camera push-pop-get-set and face-to-shape helpers now also route through `src/legacy_canvas_projection_services.*` instead of staying inline in - `src/canvas.cpp`. + `src/canvas.cpp`, while `App::draw(...)`, `App::update(...)`, + `App::terminate(...)`, `App::update_memory_usage(...)`, + `App::update_rec_frames(...)`, `App::res_from_index(...)`, + `App::res_to_index(...)`, `App::res_to_string(...)`, `App::rec_clear(...)`, + `App::rec_start(...)`, `App::rec_stop(...)`, `App::rec_export(...)`, + `App::rec_loop(...)`, and `App::render_thread_tick(...)` now also route + through `src/legacy_app_runtime_shell_services.cpp` instead of staying + inline in `src/app.cpp`, while `Canvas::draw_merge(...)`, the temporary + paint/branch orchestration helpers, final-plane composite, timelapse commit, + create/destroy, clear-context, and camera accessors now also route through + `src/legacy_canvas_render_shell_services.*` instead of staying inline in + `src/canvas.cpp`, while `Node::destroy()`, `root()`, `set_manager(...)`, + `added_to_root()`, `handle_on_screen(...)`, template loading helpers, child + add/remove/move helpers, and child query helpers now also route through + `src/legacy_ui_node_tree_services.cpp` instead of staying inline in + `src/node.cpp`. - Modern C++23 usage exists in extracted components, especially `std::span`, explicit result/status objects, and a few concepts, but the live app still does not consistently express ownership, thread affinity, or renderer diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index b6be3b59..df4a175e 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -91,7 +91,7 @@ Status: In Progress Why now: `src/canvas.cpp` is still the biggest single architectural blocker at about -1010 lines, with `src/canvas_modes.cpp` now materially thinner and the next +429 lines, with `src/canvas_modes.cpp` now materially thinner and the next remaining render-shell pressure shifting toward preview/canvas nodes. Current slice: @@ -142,6 +142,12 @@ Current slice: in `src/legacy_canvas_stroke_runtime_services.*` instead of staying inline in `src/canvas.cpp`, which trims another large retained live stroke/runtime pocket from the canvas shell. +- `Canvas::draw_merge(...)`, the temporary-paint and merge-branch + orchestration helpers, final-plane composite, timelapse commit, create, + destroy, clear-context, and camera accessors now also live in + `src/legacy_canvas_render_shell_services.*` instead of staying inline in + `src/canvas.cpp`, which trims another large retained render/context shell + from the live canvas owner. - The `CanvasModePen` and `CanvasModeLine` interaction families now also live in `src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in `src/canvas_modes.cpp`, which materially thins another retained pen/line @@ -521,7 +527,7 @@ Status: In Progress Why now: `src/app.cpp` still carries startup, frame flow, queue draining, recording, -and composition logic in one 575-line file. +and composition logic in one 292-line file. Current slice: - UI observer math now routes through `src/legacy_app_frame_services.cpp` @@ -530,6 +536,14 @@ Current slice: retained helper. - Canvas toolbar refresh in `App::update()` now also routes through that helper file, materially shrinking `src/app.cpp`. +- `App::draw(...)`, `App::update(...)`, `App::terminate(...)`, + `App::update_memory_usage(...)`, `App::update_rec_frames(...)`, + `App::res_from_index(...)`, `App::res_to_index(...)`, + `App::res_to_string(...)`, `App::rec_clear(...)`, `App::rec_start(...)`, + `App::rec_stop(...)`, `App::rec_export(...)`, `App::rec_loop(...)`, and + `App::render_thread_tick(...)` now also live in + `src/legacy_app_runtime_shell_services.cpp` instead of staying inline in + `src/app.cpp`, which materially thins the remaining frame/runtime shell. Write scope: - `src/app.cpp` @@ -758,6 +772,12 @@ Current slice: `src/legacy_ui_node_attributes.*` instead of staying inline in `src/node.cpp`, which trims another coherent XML/Yoga attribute decoding pocket from the base scene-graph shell without changing its public surface. +- `Node::destroy()`, `root()`, `set_manager(...)`, `added_to_root()`, + `handle_on_screen(...)`, template loading helpers, child add/remove/move + helpers, and child query helpers now also live in + `src/legacy_ui_node_tree_services.cpp` instead of staying inline in + `src/node.cpp`, which materially thins the remaining generic scene-graph + lifecycle/tree shell without changing the public surface. Write scope: - `src/node.cpp` diff --git a/src/app.cpp b/src/app.cpp index 28ac9fbb..3b7c2f98 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -32,43 +32,6 @@ App* App::I = nullptr; // singleton -namespace { -void apply_app_viewport(pp::renderer::gl::OpenGlViewportRect viewport) -{ - const auto status = pp::renderer::gl::apply_opengl_viewport( - viewport, - pp::renderer::gl::OpenGlViewportDispatch { - .viewport = pp::legacy::ui_gl::set_opengl_viewport, - }); - if (!status.ok()) - LOG("OpenGL viewport failed: %s", status.message); -} - -void apply_app_scissor(pp::renderer::gl::OpenGlScissorRect scissor) -{ - const auto status = pp::renderer::gl::apply_opengl_scissor_rect( - scissor, - pp::renderer::gl::OpenGlScissorDispatch { - .enable = pp::legacy::ui_gl::enable_opengl_state, - .disable = pp::legacy::ui_gl::disable_opengl_state, - .scissor = pp::legacy::ui_gl::set_opengl_scissor, - }); - if (!status.ok()) - LOG("OpenGL scissor failed: %s", status.message); -} - -void apply_app_scissor_test(bool enabled) -{ - const auto status = pp::renderer::gl::apply_opengl_scissor_test( - enabled, - pp::renderer::gl::OpenGlScissorTestDispatch { - .enable = pp::legacy::ui_gl::enable_opengl_state, - .disable = pp::legacy::ui_gl::disable_opengl_state, - }); - if (!status.ok()) - LOG("OpenGL scissor test failed: %s", status.message); -} - [[nodiscard]] GLenum linear_texture_filter() noexcept { return static_cast(pp::renderer::gl::linear_texture_filter()); @@ -94,7 +57,6 @@ void apply_app_scissor_test(bool enabled) return static_cast(pp::renderer::gl::default_framebuffer_id()); } -} void App::create() { const auto initial_surface = pp::app::plan_app_initial_surface(); @@ -182,11 +144,7 @@ void App::initLog() namespace pp::panopainter { - bool process_legacy_recording_worker_iteration(App& app); - void update_legacy_recording_frame_label(App& app); bool update_legacy_app_ui_observer(App& app, Node* n); - void watch_legacy_app_ui_children(App& app, const std::function& observer, bool skip_first_main_child); - void update_legacy_canvas_toolbar(App& app); } bool App::check_license() @@ -334,143 +292,6 @@ bool App::update_ui_observer(Node *n) return pp::panopainter::update_legacy_app_ui_observer(*this, n); } -void App::draw(float dt) -{ - const auto draw_plan = pp::app::plan_app_frame_draw( - canvas != nullptr, - canvas && canvas->m_canvas, - vr_active, - ui_visible, - vr_only); - - // update offscreen stuff - if (draw_plan.draw_canvas_stroke) - canvas->m_canvas->stroke_draw(); - - auto observer = std::bind(&App::update_ui_observer, this, std::placeholders::_1); - - if (draw_plan.draw_vr_ui) - { - uirtt.bindFramebuffer(); - uirtt.clear(); - apply_app_viewport(pp::renderer::gl::OpenGlViewportRect { - .width = static_cast(uirtt.getWidth()), - .height = static_cast(uirtt.getHeight()), - }); - apply_app_scissor_test(true); - pp::panopainter::watch_legacy_app_ui_children(*this, observer, true); - apply_app_scissor_test(false); - uirtt.unbindFramebuffer(); - } - - if (draw_plan.draw_main_ui) - { - bind_main_render_target(); - apply_app_viewport(pp::renderer::gl::OpenGlViewportRect { - .x = static_cast(off_x), - .y = static_cast(off_y), - .width = static_cast(width), - .height = static_cast(height), - }); - apply_app_scissor_test(true); - pp::panopainter::watch_legacy_app_ui_children(*this, observer, false); - apply_app_scissor_test(false); - } - - if (draw_plan.reset_redraw) - redraw = false; -} - -void App::update(float dt) -{ - const auto update_plan = pp::app::plan_app_frame_update(redraw, animate); - if (!update_plan.update_frame) - return; - - if (auto* main = layout[main_id]; update_plan.update_layouts && main) - main->update(width, height, zoom); - - if (auto* main = layout_designer[main_id]; update_plan.update_layouts && main) - main->update(width, height, zoom); - - if (!update_plan.refresh_canvas_toolbar) - return; - - pp::panopainter::update_legacy_canvas_toolbar(*this); -} - -void App::terminate() -{ - LOG("App::terminate"); - const auto shutdown_plan = pp::app::plan_app_shutdown(); - - if (shutdown_plan.save_ui_state) - ui_save(); - - if (shutdown_plan.terminate_stroke_preview_renderer) - NodeStrokePreview::terminate_renderer(); - if (shutdown_plan.stop_recording) - rec_stop(); - - if (shutdown_plan.invalidate_textures) - TextureManager::invalidate(); - if (shutdown_plan.invalidate_shaders) - ShaderManager::invalidate(); - if (shutdown_plan.unload_layouts) { - layout.unload(); - layout_designer.unload(); - } - if (shutdown_plan.destroy_ui_render_target) - uirtt.destroy(); - if (shutdown_plan.destroy_face_plane) - m_face_plane.destroy(); - if (shutdown_plan.release_panel_nodes) { - layers.reset(); - color.reset(); - stroke.reset(); - grid.reset(); - presets.reset(); - floating_presets.reset(); - floating_color.reset(); - floating_layers.reset(); - floating_picker.reset(); - } - if (shutdown_plan.clear_quick_mode_state) - quick_mode_state.clear(); -} - -void App::update_memory_usage(size_t bytes) -{ - if (auto txt = layout[main_id]->find("txt-memory")) - { - const auto label = pp::app::make_history_memory_label(bytes); - txt->set_text(label.c_str()); - } -} - -void App::update_rec_frames() -{ - pp::panopainter::update_legacy_recording_frame_label(*this); -} - -int App::res_from_index(int i) -{ - const auto resolution = pp::app::display_resolution_from_index(i); - return resolution ? resolution.value() : pp::app::document_resolution_values.front(); -} - -int App::res_to_index(int res) -{ - const auto index = pp::app::document_resolution_to_index(res); - return index ? static_cast(index.value()) : static_cast(pp::app::document_resolution_values.size()); -} - -std::string App::res_to_string(int res) -{ - const auto label = pp::app::document_resolution_label(res); - return label ? std::string(label.value()) : std::string("unknown"); -} - void App::renderdoc_frame_start() { begin_render_capture_frame(); @@ -481,64 +302,6 @@ void App::renderdoc_frame_end() end_render_capture_frame(); } -void App::rec_clear() -{ - const auto plan = pp::app::plan_recording_clear( - rec_running, - platform_deletes_recorded_files_on_clear() - ); - const auto status = pp::panopainter::execute_legacy_recording_clear_plan(*this, plan); - if (!status.ok()) - LOG("Recording clear action failed: %s", status.message); -} - -void App::rec_start() -{ - const auto plan = pp::app::plan_recording_start(rec_running); - const auto status = pp::panopainter::execute_legacy_recording_start_action(*this, plan); - if (!status.ok()) - LOG("Recording start action failed: %s", status.message); -} - -void App::rec_stop() -{ - const auto plan = pp::app::plan_recording_stop(rec_running); - const auto status = pp::panopainter::execute_legacy_recording_stop_action(*this, plan); - if (!status.ok()) - LOG("Recording stop action failed: %s", status.message); -} - -void App::rec_export(std::string path) -{ - const auto plan = pp::app::plan_recording_export(static_cast(rec_count)); -/* -#if defined(__IOS__) || defined(__OSX__) - export_mp4(rec_path, width, height, rec_count, ^(float) { - pb->increment(); - }); -#endif -*/ - const auto status = pp::panopainter::execute_legacy_recording_export_plan(*this, plan, path); - if (!status.ok()) - LOG("Recording export action failed: %s", status.message); -} - -void App::rec_loop() -{ - BT_SetTerminate(); - rec_running = true; - while (rec_running) - { - if (!pp::panopainter::process_legacy_recording_worker_iteration(*this)) - break; - } -} - -void App::render_thread_tick() -{ - runtime_.render_thread_tick(*this); -} - void App::render_thread_main() { runtime_.render_thread_main(*this, {}); diff --git a/src/canvas.cpp b/src/canvas.cpp index a13accc3..f73cc191 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1,8 +1,8 @@ #include "pch.h" #include "log.h" #include "canvas.h" -#include "app.h" -#include "legacy_canvas_draw_merge_services.h" +#include "legacy_canvas_render_shell_services.h" +#include "legacy_ui_gl_dispatch.h" #include "legacy_canvas_stroke_erase_services.h" #include "legacy_gl_renderbuffer_dispatch.h" #include "legacy_canvas_stroke_commit_services.h" @@ -14,7 +14,6 @@ #include "legacy_canvas_object_draw_services.h" #include "legacy_canvas_projection_services.h" #include "legacy_canvas_stroke_services.h" -#include "legacy_ui_gl_dispatch.h" #include "legacy_ui_overlay_services.h" #include "app_core/document_canvas.h" #include "texture.h" @@ -314,507 +313,6 @@ std::array, 6> Canvas::stroke_draw_project(std::array& layer, - const glm::mat4& ortho); - -static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution -make_canvas_draw_merge_temporary_paint_request( - Canvas& canvas, - int layer_index, - int plane_index, - const std::shared_ptr& layer, - const Brush& brush, - const glm::mat4& ortho); - -static pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution -make_canvas_draw_merge_layer_texture_dispatch( - Canvas& canvas, - int plane_index, - int layer_index); - -static pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution -make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas); - -static pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeExecution -make_canvas_draw_merge_final_plane_composite_execution(Canvas& canvas) -{ - return { - .bind_merged_texture_copy_target = [&] { - set_active_texture_unit(2); - canvas.m_merge_tex.bind(); - }, - .copy_merged_framebuffer = [&] { - copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height); - }, - .enable_blend = [&] { - apply_canvas_capability(blend_state(), true); - }, - .draw = [&] { - canvas.m_plane.draw_fill(); - }, - .bind_sampler = [&] { - canvas.m_sampler.bind(0); - }, - .bind_merged_texture = [&] { - set_active_texture_unit(0); - canvas.m_merge_tex.bind(); - }, - .unbind_merged_texture = [&] { - canvas.m_merge_tex.unbind(); - }, - }; -} - -static void execute_canvas_draw_merge_plane_final_composite( - Canvas& canvas, - const glm::mat4& ortho, - bool draw_checkerboard, - bool use_blend); - -static void execute_canvas_draw_merge_plane_dispatch( - Canvas& canvas, - int plane_index, - const std::vector>& layers, - const Brush& brush, - const glm::mat4& ortho, - bool use_blend, - bool copy_blend_destination, - bool draw_checkerboard); - -static pp::panopainter::LegacyCanvasDrawMergeLayerCompositeExecution make_canvas_draw_merge_branch_dispatch( - Canvas& canvas, - int plane_index, - int layer_index, - const std::shared_ptr& layer, - const Brush& brush, - const glm::mat4& ortho, - bool copy_blend_destination); - -static void execute_canvas_draw_merge_branch_body( - Canvas& canvas, - int plane_index, - int layer_index, - const std::shared_ptr& layer, - const Brush& brush, - const glm::mat4& ortho, - bool use_blend, - bool copy_blend_destination) -{ - if (use_blend) - { - canvas.m_merge_rtt.bindFramebuffer(); - canvas.m_merge_rtt.clear(); - } - - execute_canvas_draw_merge_layer_composite( - canvas.m_current_stroke && canvas.m_current_mode == kCanvasMode::Erase && canvas.m_show_tmp && canvas.m_current_layer_idx == layer_index, - canvas.m_current_stroke && canvas.m_show_tmp && canvas.m_current_layer_idx == layer_index, - use_blend, - make_canvas_draw_merge_branch_dispatch( - canvas, - plane_index, - layer_index, - layer, - brush, - ortho, - copy_blend_destination)); -} - -static pp::panopainter::LegacyCanvasDrawMergeLayerCompositeExecution make_canvas_draw_merge_branch_dispatch( - Canvas& canvas, - int plane_index, - int layer_index, - const std::shared_ptr& layer, - const Brush& brush, - const glm::mat4& ortho, - bool copy_blend_destination) -{ - return pp::panopainter::LegacyCanvasDrawMergeLayerCompositeExecution { - .execute_temporary_erase = [&] { - execute_canvas_draw_merge_temporary_erase_dispatch( - canvas, - plane_index, - layer_index, - layer, - ortho); - }, - .execute_temporary_paint = [&] { - pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite( - make_canvas_draw_merge_temporary_paint_request( - canvas, - layer_index, - plane_index, - layer, - brush, - ortho)); - }, - .execute_layer_texture = [&] { - execute_canvas_draw_merge_layer_texture( - pp::panopainter::LegacyCanvasDrawMergeTextureAlphaUniforms { - .mvp = ortho, - .texture_slot = 0, - .alpha = layer->m_opacity, - .highlight = layer->m_hightlight, - }, - make_canvas_draw_merge_layer_texture_dispatch( - canvas, - plane_index, - layer_index)); - }, - .execute_layer_blend = [&] { - execute_canvas_draw_merge_layer_blend( - pp::panopainter::LegacyCanvasDrawMergeLayerBlendUniforms { - .shader = { - .mvp = ortho, - .texture_slot = 0, - .destination_texture_slot = 2, - .use_destination_texture = copy_blend_destination, - .blend_mode = layer->m_blend_mode, - .alpha = 1.f, - }, - .copy_destination = copy_blend_destination, - }, - make_canvas_draw_merge_layer_blend_dispatch( - canvas)); - }, - }; -} - -static auto make_canvas_draw_merge_temporary_erase_dispatch( - Canvas& canvas, - int plane_index, - int layer_index, - const std::shared_ptr& layer, - const glm::mat4& ortho) -{ - return pp::panopainter::make_legacy_canvas_draw_merge_temporary_erase_composite( - [&] { - pp::panopainter::setup_legacy_stroke_erase_shader( - pp::panopainter::LegacyStrokeEraseUniforms { - .mvp = ortho, - .texture_slot = 0, - .stroke_texture_slot = 1, - .mask_texture_slot = 2, - .alpha = layer->m_opacity, - .mask_enabled = canvas.m_smask_active, - }); - }, - [&] { - canvas.m_sampler.bind(0); - canvas.m_sampler.bind(1); - canvas.m_sampler.bind(2); - }, - [&] { - set_active_texture_unit(0); - canvas.m_layers[layer_index]->rtt(plane_index).bindTexture(); - set_active_texture_unit(1); - canvas.m_tmp[plane_index].bindTexture(); - set_active_texture_unit(2); - canvas.m_smask.rtt(plane_index).bindTexture(); - }, - [&] { - canvas.m_plane.draw_fill(); - }, - [&] { - canvas.m_smask.rtt(plane_index).unbindTexture(); - set_active_texture_unit(1); - canvas.m_tmp[plane_index].unbindTexture(); - set_active_texture_unit(0); - canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture(); - }); -} -static void execute_canvas_draw_merge_temporary_erase_dispatch( - Canvas& canvas, - int plane_index, - int layer_index, - const std::shared_ptr& layer, - const glm::mat4& ortho) -{ - pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite( - make_canvas_draw_merge_temporary_erase_dispatch( - canvas, - plane_index, - layer_index, - layer, - ortho)); -} - -static pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution -make_canvas_draw_merge_layer_texture_dispatch( - Canvas& canvas, - int plane_index, - int layer_index) -{ - return pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution { - .bind_sampler = [&] { - canvas.m_cam_fov < 20.f ? canvas.m_sampler_nearest.bind(0) : canvas.m_sampler.bind(0); - }, - .bind_layer_texture = [&] { - set_active_texture_unit(0); - canvas.m_layers[layer_index]->rtt(plane_index).bindTexture(); - }, - .draw = [&] { - canvas.m_plane.draw_fill(); - }, - .unbind_layer_texture = [&] { - canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture(); - }, - }; -} - -static pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution -make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas) -{ - return pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution { - .unbind_merge_framebuffer = [&] { - canvas.m_merge_rtt.unbindFramebuffer(); - }, - .bind_samplers = [&] { - canvas.m_sampler.bind(0); - canvas.m_sampler.bind(2); - }, - .bind_merge_texture = [&] { - set_active_texture_unit(0); - canvas.m_merge_rtt.bindTexture(); - }, - .bind_destination_texture = [&] { - set_active_texture_unit(2); - canvas.m_merge_tex.bind(); - }, - .copy_destination_framebuffer = [&] { - copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height); - }, - .draw = [&] { - canvas.m_plane.draw_fill(); - }, - .unbind_destination_texture = [&] { - set_active_texture_unit(2); - canvas.m_merge_tex.unbind(); - }, - .unbind_merge_texture = [&] { - set_active_texture_unit(0); - canvas.m_merge_rtt.unbindTexture(); - }, - }; -} - -static void execute_canvas_draw_merge_plane_iteration( - Canvas& canvas, - const std::array& faces, - const std::vector>& layers, - const Brush& brush, - const glm::mat4& ortho, - bool use_blend, - bool copy_blend_destination, - bool draw_checkerboard) -{ - for (int plane_index = 0; plane_index < 6; plane_index++) - { - if (!faces[plane_index]) - continue; - - execute_canvas_draw_merge_plane_dispatch( - canvas, - plane_index, - layers, - brush, - ortho, - use_blend, - copy_blend_destination, - draw_checkerboard); - } -} - -static void execute_canvas_draw_merge_plane_dispatch( - Canvas& canvas, - int plane_index, - const std::vector>& layers, - const Brush& brush, - const glm::mat4& ortho, - bool use_blend, - bool copy_blend_destination, - bool draw_checkerboard) -{ - canvas.m_layers_merge.rtt(plane_index).bindFramebuffer(); - pp::panopainter::execute_legacy_canvas_draw_merge_plane_setup( - pp::panopainter::LegacyCanvasDrawMergePlaneSetupUniforms { - .checkerboard = { - .mvp = ortho, - .colorize = false, - }, - .use_blend = use_blend, - .draw_checkerboard = draw_checkerboard, - }, - pp::panopainter::LegacyCanvasDrawMergePlaneSetupExecution { - .clear_plane = [&] { - canvas.m_layers_merge.rtt(plane_index).clear({ 1, 1, 1, 0 }); - }, - .disable_blend = [&] { - apply_canvas_capability(blend_state(), false); - }, - .enable_blend = [&] { - apply_canvas_capability(blend_state(), true); - }, - .draw = [&] { - canvas.m_plane.draw_fill(); - }, - }); - - for (int layer_index = 0; layer_index < layers.size(); layer_index++) - { - execute_canvas_draw_merge_branch_body( - canvas, - plane_index, - layer_index, - layers[layer_index], - brush, - ortho, - use_blend, - copy_blend_destination); - } - - execute_canvas_draw_merge_plane_final_composite(canvas, ortho, draw_checkerboard, use_blend); - canvas.m_layers_merge.rtt(plane_index).unbindFramebuffer(); -} - -static void execute_canvas_draw_merge_plane_final_composite( - Canvas& canvas, - const glm::mat4& ortho, - bool draw_checkerboard, - bool use_blend) -{ - if (use_blend) - { - execute_canvas_draw_merge_final_plane_composite( - pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeUniforms { - .checkerboard = { - .mvp = ortho, - .colorize = false, - }, - .texture = { - .mvp = ortho, - .texture_slot = 0, - }, - .draw_checkerboard = draw_checkerboard, - }, - make_canvas_draw_merge_final_plane_composite_execution(canvas)); - } -} - -static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution -make_canvas_draw_merge_temporary_paint_request( - Canvas& canvas, - int layer_index, - int plane_index, - const std::shared_ptr& layer, - const Brush& brush, - const glm::mat4& ortho) -{ - const auto stroke_material = canvas_stroke_material_plan(brush, false); - glm::vec2 patt_scale = glm::vec2(brush.m_pattern_scale); - if (brush.m_pattern_flipx) patt_scale.x *= -1.f; - if (brush.m_pattern_flipy) patt_scale.y *= -1.f; - - return pp::panopainter::make_legacy_canvas_draw_merge_temporary_paint_composite( - [&] { - pp::panopainter::setup_legacy_stroke_composite_shader( - pp::panopainter::LegacyStrokeCompositeUniforms { - .resolution = Canvas::I->m_size, - .pattern = { - .scale = patt_scale, - .invert = static_cast(brush.m_pattern_invert), - .brightness = brush.m_pattern_brightness, - .contrast = brush.m_pattern_contrast, - .depth = brush.m_pattern_depth, - .blend_mode = brush.m_pattern_blend_mode, - .offset = Canvas::I->m_pattern_offset, - }, - .mvp = ortho, - .layer_alpha = layer->m_opacity, - .alpha_lock = layer->m_alpha_locked, - .mask_enabled = canvas.m_smask_active, - .use_fragcoord = false, - .blend_mode = brush.m_blend_mode, - .use_dual = stroke_material.composite_pass.use_dual, - .dual_blend_mode = stroke_material.composite_pass.dual_blend_mode, - .dual_alpha = stroke_material.composite_pass.dual_alpha, - .use_pattern = stroke_material.composite_pass.use_pattern, - }); - }, - [&] { - canvas.m_sampler.bind(0); - canvas.m_sampler.bind(1); - canvas.m_sampler.bind(2); - canvas.m_sampler.bind(3); - canvas.m_sampler_stencil.bind(4); - }, - [&] { - set_active_texture_unit(0); - canvas.m_layers[layer_index]->rtt(plane_index).bindTexture(); - set_active_texture_unit(1); - canvas.m_tmp[plane_index].bindTexture(); - set_active_texture_unit(2); - canvas.m_smask.rtt(plane_index).bindTexture(); - set_active_texture_unit(3); - if (stroke_material.composite_pass.use_dual) - canvas.m_tmp_dual[plane_index].bindTexture(); - set_active_texture_unit(4); - brush.m_pattern_texture ? brush.m_pattern_texture->bind() : unbind_texture_2d(); - }, - [&] { - canvas.m_plane.draw_fill(); - }, - [&] { - set_active_texture_unit(3); - if (stroke_material.composite_pass.use_dual) - canvas.m_tmp_dual[plane_index].unbindTexture(); - set_active_texture_unit(2); - canvas.m_smask.rtt(plane_index).unbindTexture(); - set_active_texture_unit(1); - canvas.m_tmp[plane_index].unbindTexture(); - set_active_texture_unit(0); - canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture(); - }); -} - void Canvas::draw_merge_temporary_paint_branch( int layer_index, int plane_index, @@ -822,14 +320,13 @@ void Canvas::draw_merge_temporary_paint_branch( const Brush& brush, const glm::mat4& ortho) { - pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite( - make_canvas_draw_merge_temporary_paint_request( - *this, - layer_index, - plane_index, - layer, - brush, - ortho)); + pp::panopainter::legacy_canvas_draw_merge_temporary_paint_branch( + *this, + layer_index, + plane_index, + std::move(layer), + brush, + ortho); } void Canvas::draw_merge_branch_orchestration( @@ -841,12 +338,7 @@ void Canvas::draw_merge_branch_orchestration( bool use_blend, bool copy_blend_destination) { - if (!(m_show_tmp && m_current_layer_idx == layer_index) && - (!layer->m_visible || - layer->m_opacity == .0f || - !layer->face(plane_index))) - return; - execute_canvas_draw_merge_branch_body( + pp::panopainter::legacy_canvas_draw_merge_branch_orchestration( *this, plane_index, layer_index, @@ -861,19 +353,10 @@ void Canvas::draw_merge_final_plane_composite( const glm::mat4& ortho, bool draw_checkerboard) { - execute_canvas_draw_merge_final_plane_composite( - pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeUniforms { - .checkerboard = { - .mvp = ortho, - .colorize = false, - }, - .texture = { - .mvp = ortho, - .texture_slot = 0, - }, - .draw_checkerboard = draw_checkerboard, - }, - make_canvas_draw_merge_final_plane_composite_execution(*this)); + pp::panopainter::legacy_canvas_draw_merge_final_plane_composite( + *this, + ortho, + draw_checkerboard); } void Canvas::stroke_draw() @@ -918,126 +401,30 @@ glm::vec3 Canvas::point_trace(glm::vec2 loc) } void Canvas::stroke_commit_timelapse() { - if (m_encoder && App::I->rec_running) - { - auto t_now = std::chrono::high_resolution_clock::now(); - float dt = std::chrono::duration(t_now - m_disrty_stroke_time).count(); - if (dt > 2.f && m_dirty_stroke && App::I->rec_mutex.try_lock()) - { - draw_merge(true); - App::I->rec_mutex.unlock(); - App::I->rec_cv.notify_one(); - LOG("rec frame generated"); - m_dirty_stroke = false; - m_disrty_stroke_time = std::chrono::steady_clock::now(); - } - } + pp::panopainter::legacy_canvas_stroke_commit_timelapse(*this); } void Canvas::draw_merge(bool draw_checkerboard, std::array faces /*= SIXPLETTE(true)*/) { - assert(App::I->is_render_thread()); - - apply_canvas_viewport(0, 0, m_width, m_height); - auto ortho = glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f, -1.f, 1.f); - const auto& b = m_current_stroke->m_brush; - - const auto blend_gate = draw_merge_blend_gate_plan( - m_width, - m_height, - m_layers, - m_current_stroke ? m_current_stroke->m_brush.get() : nullptr); - const bool use_blend = blend_gate.shader_blend; - const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color; - - // if not using shader blend, use gl rasterizer blend - apply_canvas_capability(depth_test_state(), false); - - execute_canvas_draw_merge_plane_iteration( - *this, - faces, - m_layers, - *b, - ortho, - use_blend, - copy_blend_destination, - draw_checkerboard); + pp::panopainter::legacy_canvas_draw_merge(*this, draw_checkerboard, faces); } void Canvas::stroke_update(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_update(*this, point, pressure); } void Canvas::stroke_start(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_start(*this, point, pressure); } void Canvas::destroy() { - for (int i = 0; i < 6; i++) - { - m_tmp[i].destroy(); - m_tmp_dual[i].destroy(); - m_tex[i].destroy(); - m_tex2[i].destroy(); - } - for (auto& l : m_layers) - l->destroy(); - m_smask.destroy(); - m_mixer.destroy(); - m_layers_merge.destroy(); - m_merge_rtt.destroy(); - m_merge_tex.destroy(); + pp::panopainter::legacy_canvas_destroy(*this); } bool Canvas::create(int width, int height) { - m_width = width; - m_height = height; - m_size = { width, height }; - for (int i = 0; i < 6; i++) - { - const auto stroke_format = current_canvas_stroke_internal_format(); - m_tmp[i].create(width, height, -1, stroke_format); - m_tmp_dual[i].create(width, height, -1, stroke_format); - m_tex[i].create(width, height, rgba8_internal_format()); - m_tex2[i].create(width, height, rgba8_internal_format()); - } -#if defined(__GLES__) - m_sampler_brush.create(); -#else - m_sampler_brush.create(texture_filter_linear(), texture_wrap_clamp_to_border()); -#endif - m_sampler.create(texture_filter_linear()); - m_sampler_nearest.create(texture_filter_nearest()); - m_sampler_brush.set_filter(texture_filter_linear_mipmap_linear(), texture_filter_linear()); - m_sampler_brush.set_border({ 1, 1, 1, 1 }); - m_sampler_stencil.create(texture_filter_linear(), texture_wrap_repeat()); - m_sampler_mix.create(texture_filter_nearest(), texture_wrap_repeat()); - m_sampler_linear.create(); - m_plane.create<1>(1, 1); - m_plane_brush.create<1>(1, 1); - m_brush_shape.create(); - for (auto& l : m_layers) - l->create(width, height, ""); - m_smask.create(width, height, "mask"); - //m_smask.clear({1, 1, 1, 1}); - m_layers_merge.create(width, height, "merge"); - m_merge_rtt.create(width, height); - m_merge_tex.create(width, height); - m_unsaved = true; - - timelapse_reset_encoder(); - - return true; + return pp::panopainter::legacy_canvas_create(*this, width, height); } void Canvas::clear_context() { - LOG("Canvas CLEAR CONTEXT"); - for (auto& layer : m_layers) - layer->destroy(); - for (int i = 0; i < 6; i++) - { - m_tmp[i].destroy(); - m_tex[i].destroy(); - m_tex2[i].destroy(); - } -}; + pp::panopainter::legacy_canvas_clear_context(*this); +} void Canvas::draw_objects_direct(std::function observer, Layer& layer, int frame) { @@ -1085,12 +472,12 @@ void Canvas::pop_camera() CameraData Canvas::get_camera() { - return pp::panopainter::legacy_canvas_get_camera(*this); + return pp::panopainter::legacy_canvas_render_shell_get_camera(*this); } void Canvas::set_camera(const CameraData& c) { - pp::panopainter::legacy_canvas_set_camera(*this, c); + pp::panopainter::legacy_canvas_render_shell_set_camera(*this, c); } void Canvas::timelapse_reset_encoder() noexcept diff --git a/src/legacy_app_runtime_shell_services.cpp b/src/legacy_app_runtime_shell_services.cpp new file mode 100644 index 00000000..9644bf5d --- /dev/null +++ b/src/legacy_app_runtime_shell_services.cpp @@ -0,0 +1,239 @@ +#include "pch.h" + +#include "app.h" +#include "app_core/app_frame.h" +#include "app_core/app_shutdown.h" +#include "app_core/app_status.h" +#include "legacy_recording_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "renderer_gl/opengl_capabilities.h" + +namespace { + +void apply_app_viewport(pp::renderer::gl::OpenGlViewportRect viewport) +{ + const auto status = pp::renderer::gl::apply_opengl_viewport( + viewport, + pp::renderer::gl::OpenGlViewportDispatch { + .viewport = pp::legacy::ui_gl::set_opengl_viewport, + }); + if (!status.ok()) + LOG("OpenGL viewport failed: %s", status.message); +} + +void apply_app_scissor_test(bool enabled) +{ + const auto status = pp::renderer::gl::apply_opengl_scissor_test( + enabled, + pp::renderer::gl::OpenGlScissorTestDispatch { + .enable = pp::legacy::ui_gl::enable_opengl_state, + .disable = pp::legacy::ui_gl::disable_opengl_state, + }); + if (!status.ok()) + LOG("OpenGL scissor test failed: %s", status.message); +} + +} // namespace + +namespace pp::panopainter { +bool process_legacy_recording_worker_iteration(App& app); +void update_legacy_recording_frame_label(App& app); +bool update_legacy_app_ui_observer(App& app, Node* n); +void watch_legacy_app_ui_children(App& app, const std::function& observer, bool skip_first_main_child); +void update_legacy_canvas_toolbar(App& app); +} // namespace pp::panopainter + +void App::draw(float dt) +{ + const auto draw_plan = pp::app::plan_app_frame_draw( + canvas != nullptr, + canvas && canvas->m_canvas, + vr_active, + ui_visible, + vr_only); + + // update offscreen stuff + if (draw_plan.draw_canvas_stroke) + canvas->m_canvas->stroke_draw(); + + auto observer = std::bind(&App::update_ui_observer, this, std::placeholders::_1); + + if (draw_plan.draw_vr_ui) + { + uirtt.bindFramebuffer(); + uirtt.clear(); + apply_app_viewport(pp::renderer::gl::OpenGlViewportRect { + .width = static_cast(uirtt.getWidth()), + .height = static_cast(uirtt.getHeight()), + }); + apply_app_scissor_test(true); + pp::panopainter::watch_legacy_app_ui_children(*this, observer, true); + apply_app_scissor_test(false); + uirtt.unbindFramebuffer(); + } + + if (draw_plan.draw_main_ui) + { + bind_main_render_target(); + apply_app_viewport(pp::renderer::gl::OpenGlViewportRect { + .x = static_cast(off_x), + .y = static_cast(off_y), + .width = static_cast(width), + .height = static_cast(height), + }); + apply_app_scissor_test(true); + pp::panopainter::watch_legacy_app_ui_children(*this, observer, false); + apply_app_scissor_test(false); + } + + if (draw_plan.reset_redraw) + redraw = false; +} + +void App::update(float dt) +{ + const auto update_plan = pp::app::plan_app_frame_update(redraw, animate); + if (!update_plan.update_frame) + return; + + if (auto* main = layout[main_id]; update_plan.update_layouts && main) + main->update(width, height, zoom); + + if (auto* main = layout_designer[main_id]; update_plan.update_layouts && main) + main->update(width, height, zoom); + + if (!update_plan.refresh_canvas_toolbar) + return; + + pp::panopainter::update_legacy_canvas_toolbar(*this); +} + +void App::terminate() +{ + LOG("App::terminate"); + const auto shutdown_plan = pp::app::plan_app_shutdown(); + + if (shutdown_plan.save_ui_state) + ui_save(); + + if (shutdown_plan.terminate_stroke_preview_renderer) + NodeStrokePreview::terminate_renderer(); + if (shutdown_plan.stop_recording) + rec_stop(); + + if (shutdown_plan.invalidate_textures) + TextureManager::invalidate(); + if (shutdown_plan.invalidate_shaders) + ShaderManager::invalidate(); + if (shutdown_plan.unload_layouts) { + layout.unload(); + layout_designer.unload(); + } + if (shutdown_plan.destroy_ui_render_target) + uirtt.destroy(); + if (shutdown_plan.destroy_face_plane) + m_face_plane.destroy(); + if (shutdown_plan.release_panel_nodes) { + layers.reset(); + color.reset(); + stroke.reset(); + grid.reset(); + presets.reset(); + floating_presets.reset(); + floating_color.reset(); + floating_layers.reset(); + floating_picker.reset(); + } + if (shutdown_plan.clear_quick_mode_state) + quick_mode_state.clear(); +} + +void App::update_memory_usage(size_t bytes) +{ + if (auto txt = layout[main_id]->find("txt-memory")) + { + const auto label = pp::app::make_history_memory_label(bytes); + txt->set_text(label.c_str()); + } +} + +void App::update_rec_frames() +{ + pp::panopainter::update_legacy_recording_frame_label(*this); +} + +int App::res_from_index(int i) +{ + const auto resolution = pp::app::display_resolution_from_index(i); + return resolution ? resolution.value() : pp::app::document_resolution_values.front(); +} + +int App::res_to_index(int res) +{ + const auto index = pp::app::document_resolution_to_index(res); + return index ? static_cast(index.value()) : static_cast(pp::app::document_resolution_values.size()); +} + +std::string App::res_to_string(int res) +{ + const auto label = pp::app::document_resolution_label(res); + return label ? std::string(label.value()) : std::string("unknown"); +} + +void App::rec_clear() +{ + const auto plan = pp::app::plan_recording_clear( + rec_running, + platform_deletes_recorded_files_on_clear() + ); + const auto status = pp::panopainter::execute_legacy_recording_clear_plan(*this, plan); + if (!status.ok()) + LOG("Recording clear action failed: %s", status.message); +} + +void App::rec_start() +{ + const auto plan = pp::app::plan_recording_start(rec_running); + const auto status = pp::panopainter::execute_legacy_recording_start_action(*this, plan); + if (!status.ok()) + LOG("Recording start action failed: %s", status.message); +} + +void App::rec_stop() +{ + const auto plan = pp::app::plan_recording_stop(rec_running); + const auto status = pp::panopainter::execute_legacy_recording_stop_action(*this, plan); + if (!status.ok()) + LOG("Recording stop action failed: %s", status.message); +} + +void App::rec_export(std::string path) +{ + const auto plan = pp::app::plan_recording_export(static_cast(rec_count)); +/* +#if defined(__IOS__) || defined(__OSX__) + export_mp4(rec_path, width, height, rec_count, ^(float) { + pb->increment(); + }); +#endif +*/ + const auto status = pp::panopainter::execute_legacy_recording_export_plan(*this, plan, path); + if (!status.ok()) + LOG("Recording export action failed: %s", status.message); +} + +void App::rec_loop() +{ + BT_SetTerminate(); + rec_running = true; + while (rec_running) + { + if (!pp::panopainter::process_legacy_recording_worker_iteration(*this)) + break; + } +} + +void App::render_thread_tick() +{ + runtime_.render_thread_tick(*this); +} diff --git a/src/legacy_canvas_render_shell_services.cpp b/src/legacy_canvas_render_shell_services.cpp new file mode 100644 index 00000000..2320117d --- /dev/null +++ b/src/legacy_canvas_render_shell_services.cpp @@ -0,0 +1,727 @@ +#include "pch.h" + +#include "legacy_canvas_render_shell_services.h" + +#include "app.h" +#include "legacy_canvas_draw_merge_services.h" +#include "legacy_canvas_stroke_commit_services.h" +#include "legacy_canvas_stroke_composite_services.h" +#include "legacy_canvas_stroke_erase_services.h" +#include "legacy_canvas_stroke_execution_services.h" +#include "legacy_canvas_stroke_runtime_services.h" +#include "legacy_canvas_stroke_shader_services.h" +#include "legacy_canvas_projection_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "renderer_gl/opengl_capabilities.h" +#include "util.h" + +#include +#include +#include +#include + +namespace { + +using namespace pp::panopainter; + +GLint current_canvas_stroke_internal_format() +{ + const auto renderer_features = ShaderManager::render_device_features(); + if (renderer_features.float32_linear_filtering) + return static_cast(pp::renderer::gl::rgba32f_internal_format()); + if (renderer_features.float16_render_targets) + return static_cast(pp::renderer::gl::rgba16f_internal_format()); + return static_cast(pp::renderer::gl::rgba8_internal_format()); +} + +GLint rgba8_internal_format() +{ + return static_cast(pp::renderer::gl::rgba8_internal_format()); +} + +GLenum depth_test_state() +{ + return static_cast(pp::renderer::gl::depth_test_state()); +} + +GLenum blend_state() +{ + return static_cast(pp::renderer::gl::blend_state()); +} + +void set_active_texture_unit(std::uint32_t unit_index) +{ + pp::legacy::ui_gl::activate_texture_unit(unit_index, "Canvas"); +} + +void unbind_texture_2d() +{ + pp::legacy::ui_gl::unbind_texture_2d("Canvas"); +} + +void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) +{ + pp::legacy::ui_gl::apply_viewport(x, y, width, height, "Canvas"); +} + +void apply_canvas_capability(std::uint32_t state, bool enabled) +{ + pp::legacy::ui_gl::set_capability(state, enabled, "Canvas"); +} + +pp::paint_renderer::CanvasBlendGatePlan draw_merge_blend_gate_plan( + int width, + int height, + const std::vector>& layers, + const Brush* brush) noexcept +{ + std::vector layer_blend_modes; + layer_blend_modes.reserve(layers.size()); + for (const auto& layer : layers) { + if (!layer) { + continue; + } + layer_blend_modes.push_back(layer->m_blend_mode); + } + + const auto plan = pp::paint_renderer::plan_canvas_blend_gate( + ShaderManager::render_device_features(), + pp::paint_renderer::CanvasBlendGateRequest { + .extent = pp::renderer::Extent2D { + .width = static_cast(std::max(width, 0)), + .height = static_cast(std::max(height, 0)), + }, + .layer_blend_modes = layer_blend_modes, + .has_stroke_blend_mode = brush != nullptr, + .stroke_blend_mode = brush ? brush->m_blend_mode : 0, + }); + if (plan) { + return plan.value(); + } + + pp::paint_renderer::CanvasBlendGatePlan fallback; + fallback.shader_blend = true; + fallback.complex_blend = true; + fallback.compatibility_fallback = true; + return fallback; +} + +pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeExecution +make_canvas_draw_merge_final_plane_composite_execution(Canvas& canvas) +{ + return { + .bind_merged_texture_copy_target = [&] { + set_active_texture_unit(2); + canvas.m_merge_tex.bind(); + }, + .copy_merged_framebuffer = [&] { + copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height); + }, + .enable_blend = [&] { + apply_canvas_capability(blend_state(), true); + }, + .draw = [&] { + canvas.m_plane.draw_fill(); + }, + .bind_sampler = [&] { + canvas.m_sampler.bind(0); + }, + .bind_merged_texture = [&] { + set_active_texture_unit(0); + canvas.m_merge_tex.bind(); + }, + .unbind_merged_texture = [&] { + canvas.m_merge_tex.unbind(); + }, + }; +} + +static void execute_canvas_draw_merge_temporary_erase_dispatch( + Canvas& canvas, + int plane_index, + int layer_index, + const std::shared_ptr& layer, + const glm::mat4& ortho); + +static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution +make_canvas_draw_merge_temporary_paint_request( + Canvas& canvas, + int layer_index, + int plane_index, + const std::shared_ptr& layer, + const Brush& brush, + const glm::mat4& ortho); + +static pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution +make_canvas_draw_merge_layer_texture_dispatch( + Canvas& canvas, + int plane_index, + int layer_index); + +static pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution +make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas); + +static void execute_canvas_draw_merge_branch_body( + Canvas& canvas, + int plane_index, + int layer_index, + const std::shared_ptr& layer, + const Brush& brush, + const glm::mat4& ortho, + bool use_blend, + bool copy_blend_destination) +{ + if (use_blend) + { + canvas.m_merge_rtt.bindFramebuffer(); + canvas.m_merge_rtt.clear(); + } + + pp::panopainter::execute_legacy_canvas_draw_merge_layer_composite( + canvas.m_current_stroke && canvas.m_current_mode == kCanvasMode::Erase && canvas.m_show_tmp && canvas.m_current_layer_idx == layer_index, + canvas.m_current_stroke && canvas.m_show_tmp && canvas.m_current_layer_idx == layer_index, + use_blend, + pp::panopainter::LegacyCanvasDrawMergeLayerCompositeExecution { + .execute_temporary_erase = [&] { + execute_canvas_draw_merge_temporary_erase_dispatch( + canvas, + plane_index, + layer_index, + layer, + ortho); + }, + .execute_temporary_paint = [&] { + pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite( + make_canvas_draw_merge_temporary_paint_request( + canvas, + layer_index, + plane_index, + layer, + brush, + ortho)); + }, + .execute_layer_texture = [&] { + pp::panopainter::execute_legacy_canvas_draw_merge_layer_texture( + pp::panopainter::LegacyCanvasDrawMergeTextureAlphaUniforms { + .mvp = ortho, + .texture_slot = 0, + .alpha = layer->m_opacity, + .highlight = layer->m_hightlight, + }, + make_canvas_draw_merge_layer_texture_dispatch( + canvas, + plane_index, + layer_index)); + }, + .execute_layer_blend = [&] { + pp::panopainter::execute_legacy_canvas_draw_merge_layer_blend( + pp::panopainter::LegacyCanvasDrawMergeLayerBlendUniforms { + .shader = { + .mvp = ortho, + .texture_slot = 0, + .destination_texture_slot = 2, + .use_destination_texture = copy_blend_destination, + .blend_mode = layer->m_blend_mode, + .alpha = 1.f, + }, + .copy_destination = copy_blend_destination, + }, + make_canvas_draw_merge_layer_blend_dispatch( + canvas)); + }, + }); +} + +static void execute_canvas_draw_merge_plane_final_composite( + Canvas& canvas, + const glm::mat4& ortho, + bool draw_checkerboard, + bool use_blend) +{ + if (use_blend) + { + pp::panopainter::execute_legacy_canvas_draw_merge_final_plane_composite( + pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeUniforms { + .checkerboard = { + .mvp = ortho, + .colorize = false, + }, + .texture = { + .mvp = ortho, + .texture_slot = 0, + }, + .draw_checkerboard = draw_checkerboard, + }, + make_canvas_draw_merge_final_plane_composite_execution(canvas)); + } +} + +static void execute_canvas_draw_merge_plane_dispatch( + Canvas& canvas, + int plane_index, + const std::vector>& layers, + const Brush& brush, + const glm::mat4& ortho, + bool use_blend, + bool copy_blend_destination, + bool draw_checkerboard) +{ + canvas.m_layers_merge.rtt(plane_index).bindFramebuffer(); + pp::panopainter::execute_legacy_canvas_draw_merge_plane_setup( + pp::panopainter::LegacyCanvasDrawMergePlaneSetupUniforms { + .checkerboard = { + .mvp = ortho, + .colorize = false, + }, + .use_blend = use_blend, + .draw_checkerboard = draw_checkerboard, + }, + pp::panopainter::LegacyCanvasDrawMergePlaneSetupExecution { + .clear_plane = [&] { + canvas.m_layers_merge.rtt(plane_index).clear({ 1, 1, 1, 0 }); + }, + .disable_blend = [&] { + apply_canvas_capability(blend_state(), false); + }, + .enable_blend = [&] { + apply_canvas_capability(blend_state(), true); + }, + .draw = [&] { + canvas.m_plane.draw_fill(); + }, + }); + + for (int layer_index = 0; layer_index < layers.size(); layer_index++) + { + execute_canvas_draw_merge_branch_body( + canvas, + plane_index, + layer_index, + layers[layer_index], + brush, + ortho, + use_blend, + copy_blend_destination); + } + + execute_canvas_draw_merge_plane_final_composite(canvas, ortho, draw_checkerboard, use_blend); + canvas.m_layers_merge.rtt(plane_index).unbindFramebuffer(); +} + +static void execute_canvas_draw_merge_plane_iteration( + Canvas& canvas, + const std::array& faces, + const std::vector>& layers, + const Brush& brush, + const glm::mat4& ortho, + bool use_blend, + bool copy_blend_destination, + bool draw_checkerboard) +{ + for (int plane_index = 0; plane_index < 6; plane_index++) + { + if (!faces[plane_index]) + continue; + + execute_canvas_draw_merge_plane_dispatch( + canvas, + plane_index, + layers, + brush, + ortho, + use_blend, + copy_blend_destination, + draw_checkerboard); + } +} + +static void execute_canvas_draw_merge_temporary_erase_dispatch( + Canvas& canvas, + int plane_index, + int layer_index, + const std::shared_ptr& layer, + const glm::mat4& ortho) +{ + pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite( + pp::panopainter::make_legacy_canvas_draw_merge_temporary_erase_composite( + [&] { + pp::panopainter::setup_legacy_stroke_erase_shader( + pp::panopainter::LegacyStrokeEraseUniforms { + .mvp = ortho, + .texture_slot = 0, + .stroke_texture_slot = 1, + .mask_texture_slot = 2, + .alpha = layer->m_opacity, + .mask_enabled = canvas.m_smask_active, + }); + }, + [&] { + canvas.m_sampler.bind(0); + canvas.m_sampler.bind(1); + canvas.m_sampler.bind(2); + }, + [&] { + set_active_texture_unit(0); + canvas.m_layers[layer_index]->rtt(plane_index).bindTexture(); + set_active_texture_unit(1); + canvas.m_tmp[plane_index].bindTexture(); + set_active_texture_unit(2); + canvas.m_smask.rtt(plane_index).bindTexture(); + }, + [&] { + canvas.m_plane.draw_fill(); + }, + [&] { + canvas.m_smask.rtt(plane_index).unbindTexture(); + set_active_texture_unit(1); + canvas.m_tmp[plane_index].unbindTexture(); + set_active_texture_unit(0); + canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture(); + })); +} + +static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution +make_canvas_draw_merge_temporary_paint_request( + Canvas& canvas, + int layer_index, + int plane_index, + const std::shared_ptr& layer, + const Brush& brush, + const glm::mat4& ortho) +{ + const auto stroke_material = pp::panopainter::plan_legacy_canvas_stroke_material( + pp::paint_renderer::CanvasStrokeMaterialRequest { + .destination_feedback_needed = false, + .pattern_enabled = brush.m_pattern_enabled, + .pattern_eachsample = brush.m_pattern_eachsample, + .wet_blend = brush.m_tip_wet > 0.F, + .mix_blend = brush.m_tip_mix > 0.F, + .noise_enabled = brush.m_tip_noise > 0.F, + .dual_brush_enabled = brush.m_dual_enabled, + .dual_blend_mode = brush.m_dual_blend_mode, + .pattern_blend_mode = brush.m_pattern_blend_mode, + .dual_alpha = brush.m_dual_opacity, + }); + glm::vec2 patt_scale = glm::vec2(brush.m_pattern_scale); + if (brush.m_pattern_flipx) patt_scale.x *= -1.f; + if (brush.m_pattern_flipy) patt_scale.y *= -1.f; + + return pp::panopainter::make_legacy_canvas_draw_merge_temporary_paint_composite( + [&] { + pp::panopainter::setup_legacy_stroke_composite_shader( + pp::panopainter::LegacyStrokeCompositeUniforms { + .resolution = Canvas::I->m_size, + .pattern = { + .scale = patt_scale, + .invert = static_cast(brush.m_pattern_invert), + .brightness = brush.m_pattern_brightness, + .contrast = brush.m_pattern_contrast, + .depth = brush.m_pattern_depth, + .blend_mode = brush.m_pattern_blend_mode, + .offset = Canvas::I->m_pattern_offset, + }, + .mvp = ortho, + .layer_alpha = layer->m_opacity, + .alpha_lock = layer->m_alpha_locked, + .mask_enabled = canvas.m_smask_active, + .use_fragcoord = false, + .blend_mode = brush.m_blend_mode, + .use_dual = stroke_material.composite_pass.use_dual, + .dual_blend_mode = stroke_material.composite_pass.dual_blend_mode, + .dual_alpha = stroke_material.composite_pass.dual_alpha, + .use_pattern = stroke_material.composite_pass.use_pattern, + }); + }, + [&] { + canvas.m_sampler.bind(0); + canvas.m_sampler.bind(1); + canvas.m_sampler.bind(2); + canvas.m_sampler.bind(3); + canvas.m_sampler_stencil.bind(4); + }, + [&] { + set_active_texture_unit(0); + canvas.m_layers[layer_index]->rtt(plane_index).bindTexture(); + set_active_texture_unit(1); + canvas.m_tmp[plane_index].bindTexture(); + set_active_texture_unit(2); + canvas.m_smask.rtt(plane_index).bindTexture(); + set_active_texture_unit(3); + if (stroke_material.composite_pass.use_dual) + canvas.m_tmp_dual[plane_index].bindTexture(); + set_active_texture_unit(4); + brush.m_pattern_texture ? brush.m_pattern_texture->bind() : unbind_texture_2d(); + }, + [&] { + canvas.m_plane.draw_fill(); + }, + [&] { + set_active_texture_unit(3); + if (stroke_material.composite_pass.use_dual) + canvas.m_tmp_dual[plane_index].unbindTexture(); + set_active_texture_unit(2); + canvas.m_smask.rtt(plane_index).unbindTexture(); + set_active_texture_unit(1); + canvas.m_tmp[plane_index].unbindTexture(); + set_active_texture_unit(0); + canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture(); + }); +} + +static pp::panopainter::LegacyCanvasDrawMergeLayerTextureExecution +make_canvas_draw_merge_layer_texture_dispatch( + Canvas& canvas, + int plane_index, + int layer_index) +{ + return { + .bind_sampler = [&] { + canvas.m_cam_fov < 20.f ? canvas.m_sampler_nearest.bind(0) : canvas.m_sampler.bind(0); + }, + .bind_layer_texture = [&] { + set_active_texture_unit(0); + canvas.m_layers[layer_index]->rtt(plane_index).bindTexture(); + }, + .draw = [&] { + canvas.m_plane.draw_fill(); + }, + .unbind_layer_texture = [&] { + canvas.m_layers[layer_index]->rtt(plane_index).unbindTexture(); + }, + }; +} + +static pp::panopainter::LegacyCanvasDrawMergeLayerBlendExecution +make_canvas_draw_merge_layer_blend_dispatch(Canvas& canvas) +{ + return { + .unbind_merge_framebuffer = [&] { + canvas.m_merge_rtt.unbindFramebuffer(); + }, + .bind_samplers = [&] { + canvas.m_sampler.bind(0); + canvas.m_sampler.bind(2); + }, + .bind_merge_texture = [&] { + set_active_texture_unit(0); + canvas.m_merge_rtt.bindTexture(); + }, + .bind_destination_texture = [&] { + set_active_texture_unit(2); + canvas.m_merge_tex.bind(); + }, + .copy_destination_framebuffer = [&] { + copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height); + }, + .draw = [&] { + canvas.m_plane.draw_fill(); + }, + .unbind_destination_texture = [&] { + set_active_texture_unit(2); + canvas.m_merge_tex.unbind(); + }, + .unbind_merge_texture = [&] { + set_active_texture_unit(0); + canvas.m_merge_rtt.unbindTexture(); + }, + }; +} + +} // namespace + +namespace pp::panopainter { + +void legacy_canvas_draw_merge_temporary_paint_branch( + Canvas& canvas, + int layer_index, + int plane_index, + std::shared_ptr layer, + const Brush& brush, + const glm::mat4& ortho) +{ + pp::panopainter::execute_legacy_canvas_draw_merge_temporary_composite( + make_canvas_draw_merge_temporary_paint_request( + canvas, + layer_index, + plane_index, + layer, + brush, + ortho)); +} + +void legacy_canvas_draw_merge_branch_orchestration( + Canvas& canvas, + int plane_index, + int layer_index, + const std::shared_ptr& layer, + const Brush& brush, + const glm::mat4& ortho, + bool use_blend, + bool copy_blend_destination) +{ + if (!(canvas.m_show_tmp && canvas.m_current_layer_idx == layer_index) && + (!layer->m_visible || + layer->m_opacity == .0f || + !layer->face(plane_index))) + return; + execute_canvas_draw_merge_branch_body( + canvas, + plane_index, + layer_index, + layer, + brush, + ortho, + use_blend, + copy_blend_destination); +} + +void legacy_canvas_draw_merge_final_plane_composite( + Canvas& canvas, + const glm::mat4& ortho, + bool draw_checkerboard) +{ + pp::panopainter::execute_legacy_canvas_draw_merge_final_plane_composite( + pp::panopainter::LegacyCanvasDrawMergeFinalPlaneCompositeUniforms { + .checkerboard = { + .mvp = ortho, + .colorize = false, + }, + .texture = { + .mvp = ortho, + .texture_slot = 0, + }, + .draw_checkerboard = draw_checkerboard, + }, + make_canvas_draw_merge_final_plane_composite_execution(canvas)); +} + +void legacy_canvas_stroke_commit_timelapse(Canvas& canvas) +{ + if (canvas.m_encoder && App::I->rec_running) + { + auto t_now = std::chrono::high_resolution_clock::now(); + float dt = std::chrono::duration(t_now - canvas.m_disrty_stroke_time).count(); + if (dt > 2.f && canvas.m_dirty_stroke && App::I->rec_mutex.try_lock()) + { + legacy_canvas_draw_merge(canvas, true); + App::I->rec_mutex.unlock(); + App::I->rec_cv.notify_one(); + LOG("rec frame generated"); + canvas.m_dirty_stroke = false; + canvas.m_disrty_stroke_time = std::chrono::steady_clock::now(); + } + } +} + +void legacy_canvas_draw_merge(Canvas& canvas, bool draw_checkerboard, std::array faces) +{ + assert(App::I->is_render_thread()); + + apply_canvas_viewport(0, 0, canvas.m_width, canvas.m_height); + auto ortho = glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f, -1.f, 1.f); + const auto& b = canvas.m_current_stroke->m_brush; + + const auto blend_gate = draw_merge_blend_gate_plan( + canvas.m_width, + canvas.m_height, + canvas.m_layers, + canvas.m_current_stroke ? canvas.m_current_stroke->m_brush.get() : nullptr); + const bool use_blend = blend_gate.shader_blend; + const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color; + + apply_canvas_capability(depth_test_state(), false); + + execute_canvas_draw_merge_plane_iteration( + canvas, + faces, + canvas.m_layers, + *b, + ortho, + use_blend, + copy_blend_destination, + draw_checkerboard); +} + +void legacy_canvas_destroy(Canvas& canvas) +{ + for (int i = 0; i < 6; i++) + { + canvas.m_tmp[i].destroy(); + canvas.m_tmp_dual[i].destroy(); + canvas.m_tex[i].destroy(); + canvas.m_tex2[i].destroy(); + } + for (auto& l : canvas.m_layers) + l->destroy(); + canvas.m_smask.destroy(); + canvas.m_mixer.destroy(); + canvas.m_layers_merge.destroy(); + canvas.m_merge_rtt.destroy(); + canvas.m_merge_tex.destroy(); +} + +bool legacy_canvas_create(Canvas& canvas, int width, int height) +{ + canvas.m_width = width; + canvas.m_height = height; + canvas.m_size = { width, height }; + for (int i = 0; i < 6; i++) + { + const auto stroke_format = current_canvas_stroke_internal_format(); + canvas.m_tmp[i].create(width, height, -1, stroke_format); + canvas.m_tmp_dual[i].create(width, height, -1, stroke_format); + canvas.m_tex[i].create(width, height, rgba8_internal_format()); + canvas.m_tex2[i].create(width, height, rgba8_internal_format()); + } +#if defined(__GLES__) + canvas.m_sampler_brush.create(); +#else + canvas.m_sampler_brush.create(pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::clamp_to_border_texture_wrap()); +#endif + canvas.m_sampler.create(pp::renderer::gl::linear_texture_filter()); + canvas.m_sampler_nearest.create(pp::renderer::gl::nearest_texture_filter()); + canvas.m_sampler_brush.set_filter(pp::renderer::gl::linear_mipmap_linear_texture_filter(), pp::renderer::gl::linear_texture_filter()); + canvas.m_sampler_brush.set_border({ 1, 1, 1, 1 }); + canvas.m_sampler_stencil.create(pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::repeat_texture_wrap()); + canvas.m_sampler_mix.create(pp::renderer::gl::nearest_texture_filter(), pp::renderer::gl::repeat_texture_wrap()); + canvas.m_sampler_linear.create(); + canvas.m_plane.create<1>(1, 1); + canvas.m_plane_brush.create<1>(1, 1); + canvas.m_brush_shape.create(); + for (auto& l : canvas.m_layers) + l->create(width, height, ""); + canvas.m_smask.create(width, height, "mask"); + canvas.m_layers_merge.create(width, height, "merge"); + canvas.m_merge_rtt.create(width, height); + canvas.m_merge_tex.create(width, height); + canvas.m_unsaved = true; + + canvas.timelapse_reset_encoder(); + + return true; +} + +void legacy_canvas_clear_context(Canvas& canvas) +{ + LOG("Canvas CLEAR CONTEXT"); + for (auto& layer : canvas.m_layers) + layer->destroy(); + for (int i = 0; i < 6; i++) + { + canvas.m_tmp[i].destroy(); + canvas.m_tex[i].destroy(); + canvas.m_tex2[i].destroy(); + } +} + +CameraData legacy_canvas_render_shell_get_camera(const Canvas& canvas) +{ + return legacy_canvas_get_camera(canvas); +} + +void legacy_canvas_render_shell_set_camera(Canvas& canvas, const CameraData& camera) +{ + legacy_canvas_set_camera(canvas, camera); +} + +} // namespace pp::panopainter diff --git a/src/legacy_canvas_render_shell_services.h b/src/legacy_canvas_render_shell_services.h new file mode 100644 index 00000000..4c9923fa --- /dev/null +++ b/src/legacy_canvas_render_shell_services.h @@ -0,0 +1,40 @@ +#pragma once + +#include "canvas.h" + +class Canvas; + +namespace pp::panopainter { + +void legacy_canvas_draw_merge_temporary_paint_branch( + Canvas& canvas, + int layer_index, + int plane_index, + std::shared_ptr layer, + const Brush& brush, + const glm::mat4& ortho); +void legacy_canvas_draw_merge_branch_orchestration( + Canvas& canvas, + int plane_index, + int layer_index, + const std::shared_ptr& layer, + const Brush& brush, + const glm::mat4& ortho, + bool use_blend, + bool copy_blend_destination); +void legacy_canvas_draw_merge_final_plane_composite( + Canvas& canvas, + const glm::mat4& ortho, + bool draw_checkerboard); +void legacy_canvas_stroke_commit_timelapse(Canvas& canvas); +void legacy_canvas_draw_merge( + Canvas& canvas, + bool draw_checkerboard, + std::array faces = SIXPLETTE(true)); +void legacy_canvas_destroy(Canvas& canvas); +bool legacy_canvas_create(Canvas& canvas, int width, int height); +void legacy_canvas_clear_context(Canvas& canvas); +CameraData legacy_canvas_render_shell_get_camera(const Canvas& canvas); +void legacy_canvas_render_shell_set_camera(Canvas& canvas, const CameraData& camera); + +} // namespace pp::panopainter diff --git a/src/legacy_ui_node_tree_services.cpp b/src/legacy_ui_node_tree_services.cpp new file mode 100644 index 00000000..d1665643 --- /dev/null +++ b/src/legacy_ui_node_tree_services.cpp @@ -0,0 +1,377 @@ +#include "pch.h" +#include "app.h" +#include "layout.h" +#include "node.h" + +namespace +{ +using NodeChildren = std::vector>; +using NodeChildIterator = NodeChildren::iterator; + +NodeChildIterator find_child_iterator(Node& parent, Node* child) +{ + return std::find_if(parent.m_children.begin(), parent.m_children.end(), + [child](const std::shared_ptr& candidate) { return candidate.get() == child; }); +} + +template +void attach_child(Node& parent, Node* child, int yoga_index, AttachChildrenFn&& attach_children) +{ + if (child->m_parent) + child->m_parent->remove_child(child); + + attach_children(child); + child->m_parent = &parent; + child->set_manager(parent.m_manager); + child->m_destroyed = false; + YGNodeInsertChild(parent.y_node, child->y_node, yoga_index); + child->added(&parent); + parent.on_child_added(child); +} + +template +void attach_child(Node& parent, const std::shared_ptr& child, int yoga_index, AttachChildrenFn&& attach_children) +{ + if (child->m_parent) + child->m_parent->remove_child(child.get()); + + attach_children(child); + child->m_parent = &parent; + child->set_manager(parent.m_manager); + child->m_destroyed = false; + YGNodeInsertChild(parent.y_node, child->y_node, yoga_index); + child->added(&parent); + parent.on_child_added(child.get()); +} + +void detach_child(Node& parent, NodeChildIterator child_it) +{ + Node* child = child_it->get(); + child->removed(&parent); + child->m_parent = nullptr; + YGNodeRemoveChild(parent.y_node, child->y_node); + parent.on_child_removed(child); + parent.m_children.erase(child_it); + if (parent.child_mouse_focus.get() == child) + parent.child_mouse_focus = nullptr; +} + +void detach_all_children(Node& parent) +{ + for (auto& child : parent.m_children) + { + child->removed(&parent); + child->m_parent = nullptr; + YGNodeRemoveChild(parent.y_node, child->y_node); + parent.on_child_removed(child.get()); + } + parent.m_children.clear(); + parent.child_mouse_focus = nullptr; +} + +void reorder_child(Node& parent, Node* child, int index) +{ + int count = YGNodeGetChildCount(parent.y_node); + index = glm::clamp(index, 0, count - 1); + YGNodeRemoveChild(parent.y_node, child->y_node); + YGNodeInsertChild(parent.y_node, child->y_node, index); + + auto child_it = find_child_iterator(parent, child); + auto moved_child = *child_it; + parent.m_children.erase(child_it); + parent.m_children.insert(parent.m_children.begin() + index, moved_child); +} +} + +void Node::destroy() +{ + //m_destroyed = true; + //mouse_release(); + //key_release(); + //for (auto& c : m_children) + // c->destroy(); + //for (auto p = m_parent; p; p = p->m_parent) + // if (p->child_mouse_focus.get() == this) + // p->child_mouse_focus = nullptr; + App::I->ui_task([&] + { + mouse_release(); + key_release(); + auto cp = m_children; + for (auto& c : cp) + c->destroy(); + for (auto p = m_parent; p; p = p->m_parent) + if (p->child_mouse_focus.get() == this) + p->child_mouse_focus = nullptr; + remove_all_children(); + remove_from_parent(); + }); +} + +Node* Node::root() +{ + Node* ret = this; + while (ret->m_parent) + ret = ret->m_parent; + return ret; +} + +void Node::set_manager(LayoutManager* manager) +{ + m_manager = manager; + for (auto& c : m_children) + c->set_manager(manager); +} + +bool Node::added_to_root() +{ + auto r = root(); + return r == m_manager->get(App::I->main_id); +} + +void Node::handle_on_screen(bool old_visibility, bool new_visibility) +{ + if (new_visibility == false) + { + for (auto& c : m_children) + { + if (c->m_on_screen) + { + c->handle_on_screen(true, false); + c->m_on_screen = false; + } + } + } +} + +const Node* Node::init_template(const char* id) +{ + Node* t = nullptr; + App::I->ui_task([&] + { + auto hid = const_hash(id); + Node* top = m_manager->get(hid); + t = static_cast(top->m_children[0].get()); + t->set_manager(m_manager); + for (auto& c : t->m_children) + { + auto node = c->clone(); + add_child(node); + } + YGNodeCopyStyle(y_node, t->y_node); + t->clone_copy(this); + }); + return t; +} + +bool Node::init_template_file(const std::string& path, const std::string& id) +{ + bool ret = false; + App::I->ui_task([&] + { + LayoutManager m; + if (m.load(path.c_str())) + { + auto t = m.get_ref(id.c_str())->m_children[0]; + t->set_manager(m_manager); + for (auto& c : t->m_children) + add_child(c->clone()); + YGNodeCopyStyle(y_node, t->y_node); + t->clone_copy(this); + ret = true; + } + }); + return ret; +} + +std::shared_ptr Node::load_template(const std::string& filename, const std::string& name) const +{ + LayoutManager m; + std::shared_ptr ret; + if (m.load(filename.c_str())) + ret = m.get_ref(name.c_str())->m_children[0]->clone(); + m.unload(); + return ret; +} + +std::shared_ptr Node::parse_template(const std::string& xml_string, const std::string& name) const +{ + LayoutManager m; + std::shared_ptr ret; + if (m.parse(xml_string)) + ret = m.get_ref(name.c_str())->m_children[0]->clone(); + m.unload(); + return ret; +} + +void Node::add_child(Node* n) +{ + App::I->ui_task([&] + { + attach_child(*this, n, YGNodeGetChildCount(y_node), + [this](Node* child) + { + m_children.emplace_back(child); + }); + }); +} + +void Node::add_child(Node* n, int index) +{ + App::I->ui_task([&] + { + attach_child(*this, n, index, + [this](Node* child) + { + // Preserve the current backing-vector behavior for raw-pointer inserts. + m_children.emplace_back(child); + }); + }); +} + +void Node::add_child(std::shared_ptr n) +{ + App::I->ui_task([this, n] + { + attach_child(*this, n, YGNodeGetChildCount(y_node), + [this](const std::shared_ptr& child) + { + m_children.push_back(child); + }); + }); +} + +void Node::add_child(std::shared_ptr n, int index) +{ + App::I->ui_task([&] + { + attach_child(*this, n, index, + [this, index](const std::shared_ptr& child) + { + m_children.insert(m_children.begin() + index, child); + }); + }); +} + +void Node::remove_from_parent() +{ + if (m_parent) + m_parent->remove_child(this); +} + +void Node::remove_child(Node* n) +{ + auto i = find_child_iterator(*this, n); + if (i != m_children.end()) + { + App::I->ui_task([&] + { + detach_child(*this, i); + }); + } +} + +void Node::remove_all_children() +{ + App::I->ui_task([&] + { + detach_all_children(*this); + }); +} + +void Node::move_child(Node* n, int index) +{ + App::I->ui_task([&] + { + reorder_child(*this, n, index); + }); +} + +void Node::move_child_front(Node* n) +{ + int count = YGNodeGetChildCount(y_node); + move_child(n, count - 1); +} + +void Node::move_child_offset(Node* n, int offset) +{ + int idx = get_child_index(n); + move_child(n, idx + offset); +} + +int Node::get_child_index(Node* n) +{ + int count = YGNodeGetChildCount(y_node); + for (int i = 0; i < count; i++) + { + if (YGNodeGetChild(y_node, i) == n->y_node) + { + return i; + } + } + return -1; +} + +Node* Node::get_child_at(int index) +{ + auto n = YGNodeGetChild(y_node, index); + for (auto& c : m_children) + { + if (c->y_node == n) + return c.get(); + } + return nullptr; +} + +glm::vec4 Node::get_children_rect() const +{ + if (m_children.empty()) + return glm::vec4(0); + glm::vec4 ret = m_children[0]->m_clip_uncut; + for (auto& c : m_children) + { + if (c->m_display) + { + ret = rect_union(ret, c->m_clip_uncut); + auto r = c->get_children_rect(); + if (r.w > 0 && r.z > 0) + ret = rect_union(ret, r); + } + } + return ret; +} + +std::vector> Node::get_children_at_point(glm::vec2 point) const +{ + std::vector> ret; + for (const auto& c : m_children) + { + if (point_in_rect(point, c->m_clip)) + { + ret.push_back(c); + auto c_ret = c->get_children_at_point(point); + ret.insert(ret.end(), c_ret.begin(), c_ret.end()); + } + } + return ret; +} + +bool Node::is_child_recursive(Node* o) const +{ + for (const auto& c : m_children) + { + if (c.get() == o || c->is_child_recursive(o)) + return true; + } + return false; +} + +bool Node::is_child(Node* o) const +{ + for (const auto& c : m_children) + { + if (c.get() == o) + return true; + } + return false; +} diff --git a/src/node.cpp b/src/node.cpp index 2d3df0c6..a4fde796 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -9,86 +9,6 @@ #include "util.h" #include "asset.h" -namespace -{ -using NodeChildren = std::vector>; -using NodeChildIterator = NodeChildren::iterator; - -NodeChildIterator find_child_iterator(Node& parent, Node* child) -{ - return std::find_if(parent.m_children.begin(), parent.m_children.end(), - [child](const std::shared_ptr& candidate) { return candidate.get() == child; }); -} - -template -void attach_child(Node& parent, Node* child, int yoga_index, AttachChildrenFn&& attach_children) -{ - if (child->m_parent) - child->m_parent->remove_child(child); - - attach_children(child); - child->m_parent = &parent; - child->set_manager(parent.m_manager); - child->m_destroyed = false; - YGNodeInsertChild(parent.y_node, child->y_node, yoga_index); - child->added(&parent); - parent.on_child_added(child); -} - -template -void attach_child(Node& parent, const std::shared_ptr& child, int yoga_index, AttachChildrenFn&& attach_children) -{ - if (child->m_parent) - child->m_parent->remove_child(child.get()); - - attach_children(child); - child->m_parent = &parent; - child->set_manager(parent.m_manager); - child->m_destroyed = false; - YGNodeInsertChild(parent.y_node, child->y_node, yoga_index); - child->added(&parent); - parent.on_child_added(child.get()); -} - -void detach_child(Node& parent, NodeChildIterator child_it) -{ - Node* child = child_it->get(); - child->removed(&parent); - child->m_parent = nullptr; - YGNodeRemoveChild(parent.y_node, child->y_node); - parent.on_child_removed(child); - parent.m_children.erase(child_it); - if (parent.child_mouse_focus.get() == child) - parent.child_mouse_focus = nullptr; -} - -void detach_all_children(Node& parent) -{ - for (auto& child : parent.m_children) - { - child->removed(&parent); - child->m_parent = nullptr; - YGNodeRemoveChild(parent.y_node, child->y_node); - parent.on_child_removed(child.get()); - } - parent.m_children.clear(); - parent.child_mouse_focus = nullptr; -} - -void reorder_child(Node& parent, Node* child, int index) -{ - int count = YGNodeGetChildCount(parent.y_node); - index = glm::clamp(index, 0, count - 1); - YGNodeRemoveChild(parent.y_node, child->y_node); - YGNodeInsertChild(parent.y_node, child->y_node, index); - - auto child_it = find_child_iterator(parent, child); - auto moved_child = *child_it; - parent.m_children.erase(child_it); - parent.m_children.insert(parent.m_children.begin() + index, moved_child); -} -} - void Node::app_redraw() { App::I->redraw = true; @@ -108,52 +28,6 @@ void Node::watch(std::function observer) } } -void Node::destroy() -{ - //m_destroyed = true; - //mouse_release(); - //key_release(); - //for (auto& c : m_children) - // c->destroy(); - //for (auto p = m_parent; p; p = p->m_parent) - // if (p->child_mouse_focus.get() == this) - // p->child_mouse_focus = nullptr; - App::I->ui_task([&] - { - mouse_release(); - key_release(); - auto cp = m_children; - for (auto& c : cp) - c->destroy(); - for (auto p = m_parent; p; p = p->m_parent) - if (p->child_mouse_focus.get() == this) - p->child_mouse_focus = nullptr; - remove_all_children(); - remove_from_parent(); - }); -} - -Node* Node::root() -{ - Node* ret = this; - while (ret->m_parent) - ret = ret->m_parent; - return ret; -} - -void Node::set_manager(LayoutManager* manager) -{ - m_manager = manager; - for (auto& c : m_children) - c->set_manager(manager); -} - -bool Node::added_to_root() -{ - auto r = root(); - return r == m_manager->get(App::I->main_id); -} - kEventResult Node::on_event(Event* e) { return pp::panopainter::handle_legacy_ui_node_event(*this, e); @@ -169,21 +43,6 @@ void Node::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) } -void Node::handle_on_screen(bool old_visibility, bool new_visibility) -{ - if (new_visibility == false) - { - for (auto& c : m_children) - { - if (c->m_on_screen) - { - c->handle_on_screen(true, false); - c->m_on_screen = false; - } - } - } -} - void Node::handle_parent_resize(glm::vec2 old_size, glm::vec2 new_size) { @@ -217,238 +76,6 @@ void Node::removed(Node* parent) p->child_mouse_focus = nullptr; } -const Node* Node::init_template(const char* id) -{ - Node* t = nullptr; - App::I->ui_task([&] - { - auto hid = const_hash(id); - Node* top = m_manager->get(hid); - t = static_cast(top->m_children[0].get()); - t->set_manager(m_manager); - for (auto& c : t->m_children) - { - auto node = c->clone(); - add_child(node); - } - YGNodeCopyStyle(y_node, t->y_node); - t->clone_copy(this); - }); - return t; -} - -bool Node::init_template_file(const std::string& path, const std::string& id) -{ - bool ret = false; - App::I->ui_task([&] - { - LayoutManager m; - if (m.load(path.c_str())) - { - auto t = m.get_ref(id.c_str())->m_children[0]; - t->set_manager(m_manager); - for (auto& c : t->m_children) - add_child(c->clone()); - YGNodeCopyStyle(y_node, t->y_node); - t->clone_copy(this); - ret = true; - } - }); - return ret; -} - -std::shared_ptr Node::load_template(const std::string& filename, const std::string& name) const -{ - LayoutManager m; - std::shared_ptr ret; - if (m.load(filename.c_str())) - ret = m.get_ref(name.c_str())->m_children[0]->clone(); - m.unload(); - return ret; -} - -std::shared_ptr Node::parse_template(const std::string& xml_string, const std::string& name) const -{ - LayoutManager m; - std::shared_ptr ret; - if (m.parse(xml_string)) - ret = m.get_ref(name.c_str())->m_children[0]->clone(); - m.unload(); - return ret; -} - -void Node::add_child(Node* n) -{ - App::I->ui_task([&] - { - attach_child(*this, n, YGNodeGetChildCount(y_node), - [this](Node* child) - { - m_children.emplace_back(child); - }); - }); -} - -void Node::add_child(Node* n, int index) -{ - App::I->ui_task([&] - { - attach_child(*this, n, index, - [this](Node* child) - { - // Preserve the current backing-vector behavior for raw-pointer inserts. - m_children.emplace_back(child); - }); - }); -} - -void Node::add_child(std::shared_ptr n) -{ - App::I->ui_task([this,n] - { - attach_child(*this, n, YGNodeGetChildCount(y_node), - [this](const std::shared_ptr& child) - { - m_children.push_back(child); - }); - }); -} - -void Node::add_child(std::shared_ptr n, int index) -{ - App::I->ui_task([&] - { - attach_child(*this, n, index, - [this, index](const std::shared_ptr& child) - { - m_children.insert(m_children.begin() + index, child); - }); - }); -} - -void Node::remove_from_parent() -{ - if (m_parent) - m_parent->remove_child(this); -} - -void Node::remove_child(Node* n) -{ - auto i = find_child_iterator(*this, n); - if (i != m_children.end()) - { - App::I->ui_task([&] - { - detach_child(*this, i); - }); - } -} - -void Node::remove_all_children() -{ - App::I->ui_task([&] - { - detach_all_children(*this); - }); -} - -void Node::move_child(Node* n, int index) -{ - App::I->ui_task([&] - { - reorder_child(*this, n, index); - }); -} - -void Node::move_child_front(Node* n) -{ - int count = YGNodeGetChildCount(y_node); - move_child(n, count - 1); -} - -void Node::move_child_offset(Node* n, int offset) -{ - int idx = get_child_index(n); - move_child(n, idx + offset); -} - -int Node::get_child_index(Node* n) -{ - int count = YGNodeGetChildCount(y_node); - for (int i = 0; i < count; i++) - { - if (YGNodeGetChild(y_node, i) == n->y_node) - { - return i; - } - } - return -1; -} - -Node* Node::get_child_at(int index) -{ - auto n = YGNodeGetChild(y_node, index); - for (auto& c : m_children) - { - if (c->y_node == n) - return c.get(); - } - return nullptr; -} - -glm::vec4 Node::get_children_rect() const -{ - if (m_children.empty()) - return glm::vec4(0); - glm::vec4 ret = m_children[0]->m_clip_uncut; - for (auto& c : m_children) - { - if (c->m_display) - { - ret = rect_union(ret, c->m_clip_uncut); - auto r = c->get_children_rect(); - if (r.w > 0 && r.z > 0) - ret = rect_union(ret, r); - } - } - return ret; -} - -std::vector> Node::get_children_at_point(glm::vec2 point) const -{ - std::vector> ret; - for (const auto& c : m_children) - { - if (point_in_rect(point, c->m_clip)) - { - ret.push_back(c); - auto c_ret = c->get_children_at_point(point); - ret.insert(ret.end(), c_ret.begin(), c_ret.end()); - } - } - return ret; -} - -bool Node::is_child_recursive(Node* o) const -{ - for (const auto& c : m_children) - { - if (c.get() == o || c->is_child_recursive(o)) - return true; - } - return false; -} - -bool Node::is_child(Node* o) const -{ - for (const auto& c : m_children) - { - if (c.get() == o) - return true; - } - return false; -} - void Node::mouse_capture() { pp::panopainter::legacy_ui_node_mouse_capture(*this);