Route canvas clear state through GL backend

This commit is contained in:
2026-06-04 23:13:21 +02:00
parent b8c7cd6e99
commit 9190e9053a
5 changed files with 128 additions and 54 deletions

View File

@@ -395,10 +395,13 @@ Known local toolchain state:
panorama rendering use the same tested active-texture dispatch for panorama rendering use the same tested active-texture dispatch for
texture-unit switches, and their live viewport, scissor, and generic texture-unit switches, and their live viewport, scissor, and generic
depth/blend/scissor capability changes now route through the same backend depth/blend/scissor capability changes now route through the same backend
dispatch contracts. `NodeCanvas` saved viewport/clear-color query, dispatch contracts. Retained `Canvas` stroke draw/commit, thumbnail
density/offscreen color clear, and clear-color restore paths also use tested generation, object rendering, and `LayerFrame::clear` saved viewport or
`pp_renderer_gl` dispatch helpers. Retained desktop HMD eye rendering also clear-color query plus clear-color restore paths also use tested
routes viewport execution through tested backend dispatch. `pp_renderer_gl` dispatch helpers. `NodeCanvas` saved viewport/clear-color
query, density/offscreen color clear, and clear-color restore paths use the
same helpers. Retained desktop HMD eye rendering also routes viewport
execution through tested backend dispatch.
Legacy `Texture2D`, `TextureManager`, `Sampler`, and `RTT` public headers no Legacy `Texture2D`, `TextureManager`, `Sampler`, and `RTT` public headers no
longer expose raw OpenGL enum defaults; default texture formats, sampler longer expose raw OpenGL enum defaults; default texture formats, sampler
filters/wraps, and render-target formats resolve through backend-owned filters/wraps, and render-target formats resolve through backend-owned
@@ -658,7 +661,8 @@ Known local toolchain state:
tested viewport/scissor/capability dispatch consumed by retained Canvas, tested viewport/scissor/capability dispatch consumed by retained Canvas,
NodeCanvas, NodeStrokePreview, and HMD render-state paths, NodeCanvas, NodeStrokePreview, and HMD render-state paths,
tested viewport query, clear-color query, and clear-color restore dispatch tested viewport query, clear-color query, and clear-color restore dispatch
consumed by retained `NodeCanvas` and `NodeStrokePreview` draw-state paths, consumed by retained `Canvas`, `CanvasLayer`, `NodeCanvas`, and
`NodeStrokePreview` draw-state paths,
tested color-write-mask dispatch consumed by retained `NodePanelGrid` tested color-write-mask dispatch consumed by retained `NodePanelGrid`
transparent heightmap rendering, transparent heightmap rendering,
tested pixel-buffer allocation/readback/map/unmap/delete dispatch tested pixel-buffer allocation/readback/map/unmap/delete dispatch

View File

@@ -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, and direct command execution is centralized in `src/legacy_app_shell_services.*`; SonarPen availability/startup now routes through `PlatformServices`, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and rely on the legacy platform adapter for the retained iOS SonarPen bridge | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pp_platform_api_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-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, and direct command execution is centralized in `src/legacy_app_shell_services.*`; SonarPen availability/startup now routes through `PlatformServices`, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and rely on the legacy platform adapter for the retained iOS SonarPen bridge | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pp_platform_api_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, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge 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-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, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge 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, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge 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-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, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge 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. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::query_opengl_capability_detection`, `detect_opengl_feature_state`, and `render_device_features` as the backend conversion point; that feature snapshot now includes float32-linear filtering, so canvas stroke texture format selection, renderer diagnostics, grid lightmap render planning, and grid bake target selection no longer read `ShaderManager::ext_*` flags directly. `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 and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. The retained `copy_framebuffer_to_texture_2d` utility bridge now routes 2D framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch, retained `RTT::create`/`RTT::destroy` render-target texture parameter setup, optional depth renderbuffer allocation, framebuffer allocation/attachment/status checks, binding restore, and resource deletion now route through tested `pp_renderer_gl` dispatch, retained RTT clear, masked clear with color-write-mask restore, and texture bind/unbind now route through tested `pp_renderer_gl` dispatch, retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested `pp_renderer_gl` dispatch adapters, CanvasLayer cube/equirect generation plus frame clears now route blend state, active texture units, viewport execution, color clears, and cube-face framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch adapters, `NodePanelGrid` live heightmap draw and bake setup now route depth/blend state, depth clears, color-write-mask toggles, active texture selection, and bake viewport execution through tested `pp_renderer_gl` dispatch adapters, retained CanvasMode overlay/mask/transform paths now route active texture, depth/blend state, and transform/cut viewport execution through tested `pp_renderer_gl` dispatch adapters while the retained canvas-tip pick readback remains direct legacy OpenGL, retained simple UI draw paths now share `legacy_ui_gl_dispatch` for blend-state execution, fallback 2D texture unbinds, `NodeViewport` viewport query/restore, color-buffer clears, and clear-color restore, and retained `NodeCanvas` plus `NodeStrokePreview` draw-state paths now route viewport query, clear-color query, color-buffer clear, and clear-color restore through tested `pp_renderer_gl` dispatch helpers, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and the retained `ShaderManager::ext_*` compatibility fields still use legacy OpenGL canvas/UI execution | 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 | | 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::query_opengl_capability_detection`, `detect_opengl_feature_state`, and `render_device_features` as the backend conversion point; that feature snapshot now includes float32-linear filtering, so canvas stroke texture format selection, renderer diagnostics, grid lightmap render planning, and grid bake target selection no longer read `ShaderManager::ext_*` flags directly. `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 and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. The retained `copy_framebuffer_to_texture_2d` utility bridge now routes 2D framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch, retained `RTT::create`/`RTT::destroy` render-target texture parameter setup, optional depth renderbuffer allocation, framebuffer allocation/attachment/status checks, binding restore, and resource deletion now route through tested `pp_renderer_gl` dispatch, retained RTT clear, masked clear with color-write-mask restore, and texture bind/unbind now route through tested `pp_renderer_gl` dispatch, retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested `pp_renderer_gl` dispatch adapters, CanvasLayer cube/equirect generation plus frame clears now route blend state, active texture units, viewport execution, color clears, and cube-face framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch adapters, `NodePanelGrid` live heightmap draw and bake setup now route depth/blend state, depth clears, color-write-mask toggles, active texture selection, and bake viewport execution through tested `pp_renderer_gl` dispatch adapters, retained CanvasMode overlay/mask/transform paths now route active texture, depth/blend state, and transform/cut viewport execution through tested `pp_renderer_gl` dispatch adapters while the retained canvas-tip pick readback remains direct legacy OpenGL, retained simple UI draw paths now share `legacy_ui_gl_dispatch` for blend-state execution, fallback 2D texture unbinds, `NodeViewport` viewport query/restore, color-buffer clears, and clear-color restore, retained `NodeCanvas` plus `NodeStrokePreview` draw-state paths now route viewport query, clear-color query, color-buffer clear, and clear-color restore through tested `pp_renderer_gl` dispatch helpers, and retained `Canvas` plus `CanvasLayer` stroke/object/thumbnail/frame-clear draw-state paths now route saved viewport or clear-color query and restore through the same tested helpers, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and the retained `ShaderManager::ext_*` compatibility fields still use legacy OpenGL canvas/UI execution | 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 |
| DEBT-0037 | Open | Modernization | Recording lifecycle/export planning and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, and retained `PBO` allocation/readback/map/unmap/delete operations now route through tested `pp_renderer_gl` dispatch, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, retained `App::rec_loop` readback call sites, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pp_renderer_gl_capabilities_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-clears-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback scheduling, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters | | DEBT-0037 | Open | Modernization | Recording lifecycle/export planning and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, and retained `PBO` allocation/readback/map/unmap/delete operations now route through tested `pp_renderer_gl` dispatch, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, retained `App::rec_loop` readback call sites, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pp_renderer_gl_capabilities_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-clears-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback scheduling, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters |
| DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download`/license curl helpers now ask `PlatformServices` for the Android TLS-verification bypass policy, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, progress/message UI, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pp_platform_api_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters | | DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download`/license curl helpers now ask `PlatformServices` for the Android TLS-verification bypass policy, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, progress/message UI, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pp_platform_api_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters |
| DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts before delegating import execution to `src/legacy_brush_package_import_services.*`, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter | | DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts before delegating import execution to `src/legacy_brush_package_import_services.*`, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter |

View File

@@ -1121,9 +1121,11 @@ Retained `Canvas` stroke/thumbnail/object/export paths and `NodeCanvas`
panorama rendering use the same tested active-texture dispatch for their panorama rendering use the same tested active-texture dispatch for their
texture-unit switches, and their live viewport, scissor, and generic texture-unit switches, and their live viewport, scissor, and generic
depth/blend/scissor capability changes now route through the same backend depth/blend/scissor capability changes now route through the same backend
dispatch contracts. `NodeCanvas` saved viewport/clear-color query, density dispatch contracts. Retained `Canvas` stroke draw/commit, thumbnail
target color clear, and clear-color restore paths also use tested generation, object drawing, and `LayerFrame::clear` saved viewport/clear-color
`pp_renderer_gl` dispatch helpers. query plus clear-color restore paths also use tested `pp_renderer_gl` dispatch
helpers. `NodeCanvas` saved viewport/clear-color query, density target color
clear, and clear-color restore paths use the same helpers.
Desktop HMD eye rendering now routes eye framebuffer viewport changes through Desktop HMD eye rendering now routes eye framebuffer viewport changes through
the tested `pp_renderer_gl` viewport dispatch while platform VR SDK bridges the tested `pp_renderer_gl` viewport dispatch while platform VR SDK bridges
remain isolated for later platform-shell extraction. remain isolated for later platform-shell extraction.
@@ -2075,6 +2077,10 @@ Results:
query, clear-color query, clear-color restore, and color-buffer clear helpers query, clear-color query, clear-color restore, and color-buffer clear helpers
in `NodeCanvas` and `NodeStrokePreview`, removing direct query/clear calls in `NodeCanvas` and `NodeStrokePreview`, removing direct query/clear calls
from those draw bodies while keeping their legacy compositing order. from those draw bodies while keeping their legacy compositing order.
- Retained Canvas stroke draw/commit, thumbnail, object-render, and
`LayerFrame::clear` paths now use the same tested backend viewport query,
clear-color query, and clear-color restore helpers, removing direct
viewport/clear-state queries from `src/canvas.cpp` and the frame clear path.
- Canvas draw-merge shader-blend selection now consumes the extracted - Canvas draw-merge shader-blend selection now consumes the extracted
`pp_paint_renderer` stroke composite planner for current layer and primary `pp_paint_renderer` stroke composite planner for current layer and primary
brush blend modes, while preserving legacy OpenGL compositing execution under brush blend modes, while preserving legacy OpenGL compositing execution under

View File

@@ -7,6 +7,7 @@
#include "paint_renderer/compositor.h" #include "paint_renderer/compositor.h"
#include "renderer_gl/opengl_capabilities.h" #include "renderer_gl/opengl_capabilities.h"
#include "util.h" #include "util.h"
#include <array>
#include <thread> #include <thread>
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
@@ -111,16 +112,6 @@ GLenum unsigned_byte_component_type()
return static_cast<GLenum>(pp::renderer::gl::unsigned_byte_component_type()); return static_cast<GLenum>(pp::renderer::gl::unsigned_byte_component_type());
} }
GLenum viewport_query()
{
return static_cast<GLenum>(pp::renderer::gl::viewport_query());
}
GLenum color_clear_value_query()
{
return static_cast<GLenum>(pp::renderer::gl::color_clear_value_query());
}
GLenum depth_test_state() GLenum depth_test_state()
{ {
return static_cast<GLenum>(pp::renderer::gl::depth_test_state()); return static_cast<GLenum>(pp::renderer::gl::depth_test_state());
@@ -213,6 +204,26 @@ void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std
glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height)); glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
} }
void set_opengl_clear_color(float r, float g, float b, float a) noexcept
{
glClearColor(r, g, b, a);
}
void get_opengl_integer(std::uint32_t name, std::int32_t* values) noexcept
{
GLint raw_values[4] {};
glGetIntegerv(static_cast<GLenum>(name), raw_values);
values[0] = static_cast<std::int32_t>(raw_values[0]);
values[1] = static_cast<std::int32_t>(raw_values[1]);
values[2] = static_cast<std::int32_t>(raw_values[2]);
values[3] = static_cast<std::int32_t>(raw_values[3]);
}
void get_opengl_float(std::uint32_t name, float* values) noexcept
{
glGetFloatv(static_cast<GLenum>(name), values);
}
void set_opengl_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept void set_opengl_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept
{ {
glScissor(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height)); glScissor(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
@@ -234,6 +245,41 @@ void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, s
LOG("Canvas viewport dispatch failed because: %s", status.message); LOG("Canvas viewport dispatch failed because: %s", status.message);
} }
pp::renderer::gl::OpenGlViewportRect query_canvas_viewport()
{
const auto result = pp::renderer::gl::query_opengl_viewport(
pp::renderer::gl::OpenGlViewportQueryDispatch {
.get_integer = get_opengl_integer,
});
if (!result.ok()) {
LOG("Canvas viewport query dispatch failed because: %s", result.status().message);
}
return result.value();
}
std::array<float, 4> query_canvas_clear_color()
{
const auto result = pp::renderer::gl::query_opengl_clear_color(
pp::renderer::gl::OpenGlClearColorQueryDispatch {
.get_float = get_opengl_float,
});
if (!result.ok()) {
LOG("Canvas clear-color query dispatch failed because: %s", result.status().message);
}
return result.value();
}
void apply_canvas_clear_color(std::array<float, 4> color)
{
const auto status = pp::renderer::gl::apply_opengl_clear_color(
color,
pp::renderer::gl::OpenGlClearColorDispatch {
.clear_color = set_opengl_clear_color,
});
if (!status.ok())
LOG("Canvas clear-color dispatch failed because: %s", status.message);
}
void apply_canvas_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) void apply_canvas_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{ {
const auto status = pp::renderer::gl::apply_opengl_scissor_rect( const auto status = pp::renderer::gl::apply_opengl_scissor_rect(
@@ -500,7 +546,11 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
apply_canvas_capability(scissor_test_state(), true); apply_canvas_capability(scissor_test_state(), true);
apply_canvas_capability(blend_state(), false); apply_canvas_capability(blend_state(), false);
apply_canvas_scissor(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y); apply_canvas_scissor(
static_cast<std::int32_t>(bb_min.x),
static_cast<std::int32_t>(bb_min.y),
static_cast<std::int32_t>(bb_sz.x),
static_cast<std::int32_t>(bb_sz.y));
auto layer_index = m_current_layer_idx; auto layer_index = m_current_layer_idx;
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
@@ -777,10 +827,8 @@ void Canvas::stroke_draw()
m_dirty = true; m_dirty = true;
std::array<bool, 6> merge_faces; std::array<bool, 6> merge_faces;
GLint vp[4]; const auto vp = query_canvas_viewport();
GLfloat cc[4]; const auto cc = query_canvas_clear_color();
glGetIntegerv(viewport_query(), vp);
glGetFloatv(color_clear_value_query(), cc);
const auto& brush = m_current_stroke->m_brush; const auto& brush = m_current_stroke->m_brush;
const auto& dual_brush = m_dual_stroke->m_brush; const auto& dual_brush = m_dual_stroke->m_brush;
@@ -975,8 +1023,8 @@ void Canvas::stroke_draw()
m_sampler_nearest.unbind(); m_sampler_nearest.unbind();
m_sampler_stencil.unbind(); m_sampler_stencil.unbind();
apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
glClearColor(cc[0], cc[1], cc[2], cc[3]); apply_canvas_clear_color(cc);
if (m_commit_delayed) if (m_commit_delayed)
{ {
@@ -1086,10 +1134,8 @@ void Canvas::stroke_commit()
App::I->redraw = true; App::I->redraw = true;
// save viewport and clear color states // save viewport and clear color states
GLint vp[4]; const auto vp = query_canvas_viewport();
GLfloat cc[4]; const auto cc = query_canvas_clear_color();
glGetIntegerv(viewport_query(), vp);
glGetFloatv(color_clear_value_query(), cc);
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// allocate action to add to history // allocate action to add to history
@@ -1259,8 +1305,8 @@ void Canvas::stroke_commit()
// restore viewport and clear color states // restore viewport and clear color states
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
glClearColor(cc[0], cc[1], cc[2], cc[3]); apply_canvas_clear_color(cc);
set_active_texture_unit(0); set_active_texture_unit(0);
// save history // save history
@@ -2962,10 +3008,8 @@ Image Canvas::thumbnail_generate(int w, int h)
App::I->render_task([this, w, h, &image] App::I->render_task([this, w, h, &image]
{ {
// save viewport and clear color states // save viewport and clear color states
GLint vp[4]; const auto vp = query_canvas_viewport();
GLfloat cc[4]; const auto cc = query_canvas_clear_color();
glGetIntegerv(viewport_query(), vp);
glGetFloatv(color_clear_value_query(), cc);
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// prepare common states // prepare common states
@@ -3057,8 +3101,8 @@ Image Canvas::thumbnail_generate(int w, int h)
// restore viewport and clear color states // restore viewport and clear color states
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
glClearColor(cc[0], cc[1], cc[2], cc[3]); apply_canvas_clear_color(cc);
set_active_texture_unit(0); set_active_texture_unit(0);
}); });
@@ -3097,10 +3141,8 @@ void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, con
App::I->render_task([&] App::I->render_task([&]
{ {
// save viewport and clear color states // save viewport and clear color states
GLint vp[4]; const auto vp = query_canvas_viewport();
GLfloat cc[4]; const auto cc = query_canvas_clear_color();
glGetIntegerv(viewport_query(), vp);
glGetFloatv(color_clear_value_query(), cc);
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// prepare common states // prepare common states
@@ -3129,8 +3171,8 @@ void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, con
// restore viewport and clear color states // restore viewport and clear color states
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
glClearColor(cc[0], cc[1], cc[2], cc[3]); apply_canvas_clear_color(cc);
set_active_texture_unit(0); set_active_texture_unit(0);
}); });
} }
@@ -3140,10 +3182,8 @@ void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm:
App::I->render_task([&] App::I->render_task([&]
{ {
// save viewport and clear color states // save viewport and clear color states
GLint vp[4]; const auto vp = query_canvas_viewport();
GLfloat cc[4]; const auto cc = query_canvas_clear_color();
glGetIntegerv(viewport_query(), vp);
glGetFloatv(color_clear_value_query(), cc);
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// prepare common states // prepare common states
@@ -3235,8 +3275,8 @@ void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm:
// restore viewport and clear color states // restore viewport and clear color states
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
glClearColor(cc[0], cc[1], cc[2], cc[3]); apply_canvas_clear_color(cc);
set_active_texture_unit(0); set_active_texture_unit(0);
}); });
} }

View File

@@ -6,6 +6,8 @@
#include "rtt.h" #include "rtt.h"
#include "util.h" #include "util.h"
#include <array>
uint32_t Layer::s_count = 0; uint32_t Layer::s_count = 0;
namespace { namespace {
@@ -40,6 +42,11 @@ void clear_opengl_buffer(std::uint32_t mask) noexcept
glClear(static_cast<GLbitfield>(mask)); glClear(static_cast<GLbitfield>(mask));
} }
void get_opengl_float(std::uint32_t name, float* values) noexcept
{
glGetFloatv(static_cast<GLenum>(name), values);
}
void copy_opengl_tex_sub_image_2d( void copy_opengl_tex_sub_image_2d(
std::uint32_t target, std::uint32_t target,
std::int32_t level, std::int32_t level,
@@ -142,9 +149,27 @@ void clear_layer_color_buffer(const glm::vec4& color)
LOG("Layer clear dispatch failed because: %s", status.message); LOG("Layer clear dispatch failed because: %s", status.message);
} }
void restore_layer_clear_color(const GLfloat* color) std::array<float, 4> query_layer_clear_color()
{ {
set_opengl_clear_color(color[0], color[1], color[2], color[3]); const auto result = pp::renderer::gl::query_opengl_clear_color(
pp::renderer::gl::OpenGlClearColorQueryDispatch {
.get_float = get_opengl_float,
});
if (!result.ok()) {
LOG("Layer clear-color query dispatch failed because: %s", result.status().message);
}
return result.value();
}
void restore_layer_clear_color(std::array<float, 4> color)
{
const auto status = pp::renderer::gl::apply_opengl_clear_color(
color,
pp::renderer::gl::OpenGlClearColorDispatch {
.clear_color = set_opengl_clear_color,
});
if (!status.ok())
LOG("Layer clear-color dispatch failed because: %s", status.message);
} }
} }
@@ -601,8 +626,7 @@ void LayerFrame::clear(const glm::vec4& c)
App::I->render_task([&] App::I->render_task([&]
{ {
// push clear color state // push clear color state
GLfloat cc[4]; const auto cc = query_layer_clear_color();
glGetFloatv(pp::renderer::gl::color_clear_value_query(), cc);
bool erase = (c.a == 0.f); bool erase = (c.a == 0.f);