From bc5b39057da6bb916fb60b8cddb2d2f1b9ed28b5 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Wed, 3 Jun 2026 18:32:17 +0200 Subject: [PATCH] Publish renderer feature snapshot for canvas gates --- docs/modernization/debt.md | 2 +- docs/modernization/roadmap.md | 8 ++++++++ src/app_shaders.cpp | 14 ++++++++++++++ src/canvas.cpp | 5 +---- src/node_canvas.cpp | 5 +---- src/shader.cpp | 11 +++++++++++ src/shader.h | 4 ++++ 7 files changed, 40 insertions(+), 9 deletions(-) diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 22780d5..0db381f 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -53,7 +53,7 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0033 | Open | Modernization | Tools menu planning and direct command execution dispatch now consume pure `pp_app_core` through `App::init_menu_tools`, `pano_cli plan-tools-menu`, `pano_cli plan-tools-panel`, and the `ToolsMenuServices` boundary, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and call the iOS SonarPen bridge directly | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pano_cli plan-tools-menu --command shortcuts`; `pano_cli plan-tools-panel --panel layers`; `pano_cli plan-tools-panel --panel animation --already-visible`; `ctest --preset desktop-fast --build-config Debug` | Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with `App::init_menu_tools` acting only as a UI adapter and no legacy Tools adapter | | DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, but the live adapter still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter | | DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary, and history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, but the live adapter still opens legacy open/save/settings/message-box dialogs and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter | -| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. `pp_paint_renderer::plan_canvas_blend_gate` now owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it for their existing shader-blend gates. Actual live stroke compositing, dual-brush feedback, and pattern feedback still use the legacy OpenGL canvas path | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior | +| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::render_device_features` as the backend conversion point. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates. Actual live stroke compositing, dual-brush feedback, and pattern feedback still use the legacy OpenGL canvas path | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior | ## Closed Debt diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index cf7e135..eb4cd08 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -883,6 +883,10 @@ mapping from persisted layer and brush blend indices to that planner, including fallback behavior for unknown nonzero indices. Both `Canvas::draw_merge` and `NodeCanvas` panorama rendering consume that shared gate, so the live app no longer has duplicate local blend-trigger logic. +The OpenGL shader initialization path now stores a renderer-neutral +`RenderDeviceFeatures` snapshot converted by `pp_renderer_gl`, and those live +canvas gates consume that snapshot instead of rebuilding feature flags from +individual `ShaderManager` extension booleans. The existing renderer classes are not yet fully behind the renderer interfaces. @@ -1610,6 +1614,10 @@ Results: - `NodeCanvas` panorama rendering now consumes the same tested `pp_paint_renderer` canvas blend-gate planner as `Canvas::draw_merge`, so layer and primary-brush blend-trigger compatibility is centralized. +- Shader initialization now publishes the OpenGL backend's renderer-neutral + feature snapshot through the legacy shader manager, and live canvas blend + gates consume that `RenderDeviceFeatures` value instead of hand-built + framebuffer-fetch/texture-copy flags. - Canvas equirectangular import drawing and depth export rendering now route depth/blend state and active texture units through the renderer GL backend mapping. diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp index 750a206..51bb2cc 100644 --- a/src/app_shaders.cpp +++ b/src/app_shaders.cpp @@ -16,6 +16,17 @@ namespace { return static_cast(pp::renderer::gl::extension_string_name()); } +[[nodiscard]] pp::renderer::gl::OpenGlCapabilities shader_manager_capabilities() noexcept +{ + pp::renderer::gl::OpenGlCapabilities capabilities; + capabilities.framebuffer_fetch = ShaderManager::ext_framebuffer_fetch; + capabilities.map_buffer_alignment = ShaderManager::ext_map_aligned; + capabilities.float32_textures = ShaderManager::ext_float32; + capabilities.float32_linear = ShaderManager::ext_float32_linear; + capabilities.float16_textures = ShaderManager::ext_float16; + return capabilities; +} + } void App::initShaders() @@ -55,6 +66,7 @@ void App::initShaders() ShaderManager::ext_float32 = capabilities.float32_textures; ShaderManager::ext_float32_linear = capabilities.float32_linear; ShaderManager::ext_float16 = capabilities.float16_textures; + ShaderManager::set_render_device_features(pp::renderer::gl::render_device_features(capabilities)); }); #if __GL__ @@ -63,6 +75,8 @@ void App::initShaders() ShaderManager::ext_float32 = true; ShaderManager::ext_float16 = true; #endif + ShaderManager::set_render_device_features( + pp::renderer::gl::render_device_features(shader_manager_capabilities())); LOG("Shader Extension shader_framebuffer_fetch: %s", ShaderManager::ext_framebuffer_fetch ? "enabled" : "disabled"); diff --git a/src/canvas.cpp b/src/canvas.cpp index 108e5d1..ff17326 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -46,10 +46,7 @@ GLenum rgba_pixel_format() pp::renderer::RenderDeviceFeatures canvas_stroke_composite_features() noexcept { - return pp::renderer::RenderDeviceFeatures { - .framebuffer_fetch = ShaderManager::ext_framebuffer_fetch, - .texture_copy = !ShaderManager::ext_framebuffer_fetch, - }; + return ShaderManager::render_device_features(); } bool draw_merge_needs_shader_blend( diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 03f09f2..89965d7 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -29,10 +29,7 @@ void unbind_texture_2d() pp::renderer::RenderDeviceFeatures node_canvas_stroke_composite_features() noexcept { - return pp::renderer::RenderDeviceFeatures { - .framebuffer_fetch = ShaderManager::ext_framebuffer_fetch, - .texture_copy = !ShaderManager::ext_framebuffer_fetch, - }; + return ShaderManager::render_device_features(); } bool node_canvas_needs_shader_blend( diff --git a/src/shader.cpp b/src/shader.cpp index 176fae7..ee8d92c 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -211,6 +211,7 @@ std::int32_t get_opengl_uniform_location(std::uint32_t program, const char* name std::map ShaderManager::m_shaders; Shader* ShaderManager::m_current; +pp::renderer::RenderDeviceFeatures ShaderManager::m_render_device_features {}; bool ShaderManager::ext_framebuffer_fetch = false; bool ShaderManager::ext_float32 = false; bool ShaderManager::ext_float32_linear = false; @@ -816,6 +817,16 @@ void ShaderManager::u_float(kShaderUniform id, float f) m_current->u_float(id, f); } +void ShaderManager::set_render_device_features(pp::renderer::RenderDeviceFeatures features) noexcept +{ + m_render_device_features = features; +} + +pp::renderer::RenderDeviceFeatures ShaderManager::render_device_features() noexcept +{ + return m_render_device_features; +} + void ShaderManager::invalidate() { m_shaders.clear(); diff --git a/src/shader.h b/src/shader.h index d44bd40..619bd12 100644 --- a/src/shader.h +++ b/src/shader.h @@ -1,4 +1,5 @@ #pragma once +#include "renderer_api/renderer_api.h" #include "util.h" bool check_uniform_uniqueness(); @@ -108,6 +109,7 @@ class ShaderManager { static std::map m_shaders; static Shader* m_current; + static pp::renderer::RenderDeviceFeatures m_render_device_features; public: static bool ext_framebuffer_fetch; static bool ext_float32; @@ -127,5 +129,7 @@ public: static void u_int(kShaderUniform id, int i); static void u_int(const char* name, int i); static void u_float(kShaderUniform id, float f); + static void set_render_device_features(pp::renderer::RenderDeviceFeatures features) noexcept; + static pp::renderer::RenderDeviceFeatures render_device_features() noexcept; static void invalidate(); };