Route paint render state through GL backend

This commit is contained in:
2026-06-04 22:19:54 +02:00
parent 24197c5f7e
commit d55f26d637
7 changed files with 304 additions and 73 deletions

View File

@@ -383,9 +383,15 @@ Known local toolchain state:
`NodeStrokePreview` brush preview rendering also consumes backend-owned `NodeStrokePreview` brush preview rendering also consumes backend-owned
depth/scissor/blend state, viewport/clear-color queries, active texture depth/scissor/blend state, viewport/clear-color queries, active texture
unit execution, fallback 2D texture unbinds, 2D texture targets, copy unit execution, fallback 2D texture unbinds, 2D texture targets, copy
targets, and sampler filters/wraps. Retained `Canvas` stroke/thumbnail paths targets, and sampler filters/wraps. Its live stroke-mixer and brush-preview
and `NodeCanvas` panorama rendering use the same tested active-texture viewport, scissor, and depth/blend state changes now execute through tested
dispatch for texture-unit switches. `pp_renderer_gl` dispatch with only local OpenGL adapter endpoints retained.
Retained `Canvas` stroke/thumbnail/object/export paths and `NodeCanvas`
panorama rendering use the same tested active-texture dispatch for
texture-unit switches, and their live viewport, scissor, and generic
depth/blend/scissor capability changes now route through the same backend
dispatch contracts. 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
@@ -639,6 +645,8 @@ Known local toolchain state:
masked color clear with color-write-mask restore, and texture-bind dispatch, masked color clear with color-write-mask restore, and texture-bind dispatch,
tested active-texture dispatch consumed by retained Canvas, NodeCanvas, and tested active-texture dispatch consumed by retained Canvas, NodeCanvas, and
NodeStrokePreview texture-unit switches, NodeStrokePreview texture-unit switches,
tested viewport/scissor/capability dispatch consumed by retained Canvas,
NodeCanvas, NodeStrokePreview, and HMD render-state paths,
tested pixel-buffer allocation/readback/map/unmap/delete dispatch tested pixel-buffer allocation/readback/map/unmap/delete dispatch
consumed by retained `PBO` recording readbacks, tested framebuffer-to-texture consumed by retained `PBO` recording readbacks, tested framebuffer-to-texture
2D copy dispatch consumed by retained canvas/UI paint paths, tested framebuffer 2D copy dispatch consumed by retained canvas/UI paint paths, tested framebuffer

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, and retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, the retained cube-map framebuffer copy, 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, and retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested `pp_renderer_gl` dispatch adapters, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, the retained cube-map framebuffer copy, 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

@@ -1108,9 +1108,17 @@ active texture units to `pp_renderer_gl`.
state, viewport/clear-color queries, active texture unit execution, fallback state, viewport/clear-color queries, active texture unit execution, fallback
2D texture unbinds, 2D texture targets, copy targets, sampler filters/wraps, 2D texture unbinds, 2D texture targets, copy targets, sampler filters/wraps,
and destination-feedback copy/fetch decisions to `pp_renderer_gl` and and destination-feedback copy/fetch decisions to `pp_renderer_gl` and
`pp_paint_renderer`. Retained `Canvas` stroke/thumbnail paths and `NodeCanvas` `pp_paint_renderer`. Its live stroke-mixer and brush-preview viewport,
scissor, and depth/blend state changes now also execute through tested
`pp_renderer_gl` dispatch with only local OpenGL adapter endpoints retained.
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. texture-unit switches, and their live viewport, scissor, and generic
depth/blend/scissor capability changes now route through the same backend
dispatch contracts.
Desktop HMD eye rendering now routes eye framebuffer viewport changes through
the tested `pp_renderer_gl` viewport dispatch while platform VR SDK bridges
remain isolated for later platform-shell extraction.
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 are resolved through backend-owned filters/wraps, and render-target formats are resolved through backend-owned
@@ -2005,6 +2013,8 @@ Results:
blend/depth state snapshots and restores, depth clears, active texture units, blend/depth state snapshots and restores, depth clears, active texture units,
and fallback 2D texture unbinds through the renderer GL backend mapping; and fallback 2D texture unbinds through the renderer GL backend mapping;
platform VR SDK bridges remain isolated for later platform-shell extraction. platform VR SDK bridges remain isolated for later platform-shell extraction.
Eye framebuffer viewport execution in the retained HMD path also routes
through tested `pp_renderer_gl` viewport dispatch.
- Canvas mode overlay, mask, and transform paths now route generic OpenGL - Canvas mode overlay, mask, and transform paths now route generic OpenGL
blend/depth state, active texture units, 2D framebuffer-to-texture copy blend/depth state, active texture units, 2D framebuffer-to-texture copy
dispatch, RGBA8 readback formats, and RTT-backed transform history region dispatch, RGBA8 readback formats, and RTT-backed transform history region
@@ -2013,6 +2023,8 @@ Results:
viewport/clear/blend/depth/scissor state, color clears, active texture units, viewport/clear/blend/depth/scissor state, color clears, active texture units,
fallback 2D texture unbinds, 2D framebuffer-to-texture copy dispatch, and fallback 2D texture unbinds, 2D framebuffer-to-texture copy dispatch, and
RGBA8 render-target formats through the renderer GL backend mapping. RGBA8 render-target formats through the renderer GL backend mapping.
Its live viewport and generic blend/depth/scissor capability changes now
execute through tested `pp_renderer_gl` dispatch adapters.
- Canvas resource setup now routes stroke-buffer RGBA8/RGBA16F/RGBA32F - Canvas resource setup now routes stroke-buffer RGBA8/RGBA16F/RGBA32F
formats, flood-fill texture upload format/type, brush/stencil/mix sampler formats, flood-fill texture upload format/type, brush/stencil/mix sampler
filters and wraps, and cube-strip import channel formats through the renderer filters and wraps, and cube-strip import channel formats through the renderer
@@ -2021,7 +2033,8 @@ Results:
- Early canvas draw helpers now route pick readbacks, stroke mixer depth/scissor - Early canvas draw helpers now route pick readbacks, stroke mixer depth/scissor
and blend state, saved viewport/clear-state queries, active texture units, and blend state, saved viewport/clear-state queries, active texture units,
fallback 2D texture unbinds, and stroke background copy targets through the fallback 2D texture unbinds, and stroke background copy targets through the
renderer GL backend mapping. renderer GL backend mapping. Stroke mixer viewport/scissor execution also
routes through the tested backend dispatch contract.
- Canvas stroke commit now routes saved viewport/clear/blend state, history - Canvas stroke commit now routes saved viewport/clear/blend state, history
readbacks, active texture units, fallback 2D texture unbinds, and layer readbacks, active texture units, fallback 2D texture unbinds, and layer
compositing copy targets through the renderer GL backend mapping; the compositing copy targets through the renderer GL backend mapping; the
@@ -2062,6 +2075,10 @@ Results:
framebuffer copy targets, and depth renderbuffer allocation plus framebuffer framebuffer copy targets, and depth renderbuffer allocation plus framebuffer
depth attach/detach through tested renderer GL backend dispatch contracts; depth attach/detach through tested renderer GL backend dispatch contracts;
`src/canvas.cpp` no longer contains raw `GL_*` constants. `src/canvas.cpp` no longer contains raw `GL_*` constants.
- Retained Canvas, NodeCanvas, NodeStrokePreview, and HMD viewport/scissor/
capability execution now compiles through the renderer GL backend dispatch
adapters with `pp_legacy_paint_document`, `pp_panopainter_ui`, and
`panopainter_app`.
- Windows desktop OpenGL context creation now consumes a tested - Windows desktop OpenGL context creation now consumes a tested
`windows_wgl_core_context_3_3_config()` catalog from `pp_renderer_gl`, moving `windows_wgl_core_context_3_3_config()` catalog from `pp_renderer_gl`, moving
the active WGL context/pixel-format attribute literals out of the platform the active WGL context/pixel-format attribute literals out of the platform

View File

@@ -198,6 +198,74 @@ void unbind_texture_2d()
LOG("Canvas texture unbind dispatch failed because: %s", status.message); LOG("Canvas texture unbind dispatch failed because: %s", status.message);
} }
void enable_opengl_state(std::uint32_t state) noexcept
{
glEnable(static_cast<GLenum>(state));
}
void disable_opengl_state(std::uint32_t state) noexcept
{
glDisable(static_cast<GLenum>(state));
}
void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept
{
glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
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));
}
void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
const auto status = pp::renderer::gl::apply_opengl_viewport(
pp::renderer::gl::OpenGlViewportRect {
.x = x,
.y = y,
.width = width,
.height = height,
},
pp::renderer::gl::OpenGlViewportDispatch {
.viewport = set_opengl_viewport,
});
if (!status.ok())
LOG("Canvas viewport 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)
{
const auto status = pp::renderer::gl::apply_opengl_scissor_rect(
pp::renderer::gl::OpenGlScissorRect {
.enabled = 1U,
.x = x,
.y = y,
.width = width,
.height = height,
},
pp::renderer::gl::OpenGlScissorDispatch {
.enable = enable_opengl_state,
.disable = disable_opengl_state,
.scissor = set_opengl_scissor,
});
if (!status.ok())
LOG("Canvas scissor dispatch failed because: %s", status.message);
}
void apply_canvas_capability(std::uint32_t state, bool enabled)
{
const auto status = pp::renderer::gl::apply_opengl_capability(
state,
enabled,
pp::renderer::gl::OpenGlCapabilityDispatch {
.enable = enable_opengl_state,
.disable = disable_opengl_state,
});
if (!status.ok())
LOG("Canvas capability dispatch failed because: %s", status.message);
}
void gen_opengl_renderbuffers(std::uint32_t count, std::uint32_t* ids) noexcept void gen_opengl_renderbuffers(std::uint32_t count, std::uint32_t* ids) noexcept
{ {
glGenRenderbuffers(static_cast<GLsizei>(count), reinterpret_cast<GLuint*>(ids)); glGenRenderbuffers(static_cast<GLsizei>(count), reinterpret_cast<GLuint*>(ids));
@@ -427,12 +495,12 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz)
m_mixer.bindFramebuffer(); m_mixer.bindFramebuffer();
glViewport(0, 0, m_mixer.getWidth(), m_mixer.getHeight()); apply_canvas_viewport(0, 0, m_mixer.getWidth(), m_mixer.getHeight());
glDisable(depth_test_state()); apply_canvas_capability(depth_test_state(), false);
glEnable(scissor_test_state()); apply_canvas_capability(scissor_test_state(), true);
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
glScissor(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y); apply_canvas_scissor(bb_min.x, bb_min.y, bb_sz.x, 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++)
@@ -718,7 +786,7 @@ void Canvas::stroke_draw()
const auto& dual_brush = m_dual_stroke->m_brush; const auto& dual_brush = m_dual_stroke->m_brush;
auto ortho_proj = glm::ortho(0.f, (float)m_width, 0.f, (float)m_height, -1.f, 1.f); auto ortho_proj = glm::ortho(0.f, (float)m_width, 0.f, (float)m_height, -1.f, 1.f);
glViewport(0, 0, m_width, m_height); apply_canvas_viewport(0, 0, m_width, m_height);
m_sampler_brush.bind(0); m_sampler_brush.bind(0);
m_sampler_nearest.bind(1); m_sampler_nearest.bind(1);
@@ -733,7 +801,7 @@ void Canvas::stroke_draw()
const auto stroke_feedback = canvas_destination_feedback_plan(m_width, m_height); const auto stroke_feedback = canvas_destination_feedback_plan(m_width, m_height);
const bool copy_stroke_destination = !stroke_feedback.reads_destination_color; const bool copy_stroke_destination = !stroke_feedback.reads_destination_color;
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
ShaderManager::use(kShader::Stroke); ShaderManager::use(kShader::Stroke);
ShaderManager::u_int(kShaderUniform::Tex, 0); // brush ShaderManager::u_int(kShaderUniform::Tex, 0); // brush
if (copy_stroke_destination) if (copy_stroke_destination)
@@ -907,7 +975,7 @@ void Canvas::stroke_draw()
m_sampler_nearest.unbind(); m_sampler_nearest.unbind();
m_sampler_stencil.unbind(); m_sampler_stencil.unbind();
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
if (m_commit_delayed) if (m_commit_delayed)
@@ -1032,8 +1100,8 @@ void Canvas::stroke_commit()
App::I->title_update(); App::I->title_update();
// prepare common states // prepare common states
glViewport(0, 0, m_width, m_height); apply_canvas_viewport(0, 0, m_width, m_height);
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
const auto& b = m_current_stroke->m_brush; const auto& b = m_current_stroke->m_brush;
@@ -1190,8 +1258,8 @@ void Canvas::stroke_commit()
} }
// restore viewport and clear color states // restore viewport and clear color states
blend ? glEnable(blend_state()) : glDisable(blend_state()); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
set_active_texture_unit(0); set_active_texture_unit(0);
@@ -1226,7 +1294,7 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SI
{ {
assert(App::I->is_render_thread()); assert(App::I->is_render_thread());
glViewport(0, 0, m_width, m_height); apply_canvas_viewport(0, 0, m_width, m_height);
auto ortho = glm::ortho<float>(-0.5f, 0.5f, -0.5f, 0.5f, -1.f, 1.f); auto ortho = glm::ortho<float>(-0.5f, 0.5f, -0.5f, 0.5f, -1.f, 1.f);
const auto& b = m_current_stroke->m_brush; const auto& b = m_current_stroke->m_brush;
@@ -1239,7 +1307,7 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SI
const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color; const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color;
// if not using shader blend, use gl rasterizer blend // if not using shader blend, use gl rasterizer blend
glDisable(depth_test_state()); apply_canvas_capability(depth_test_state(), false);
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
{ {
@@ -1251,7 +1319,7 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SI
if (use_blend) if (use_blend)
{ {
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
m_layers_merge.rtt(plane_index).clear(); m_layers_merge.rtt(plane_index).clear();
} }
else else
@@ -1263,7 +1331,7 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SI
ShaderManager::u_mat4(kShaderUniform::MVP, ortho); ShaderManager::u_mat4(kShaderUniform::MVP, ortho);
m_plane.draw_fill(); m_plane.draw_fill();
} }
glEnable(blend_state()); apply_canvas_capability(blend_state(), true);
} }
for (int layer_index = 0; layer_index < m_layers.size(); layer_index++) for (int layer_index = 0; layer_index < m_layers.size(); layer_index++)
@@ -1434,7 +1502,7 @@ void Canvas::draw_merge(bool draw_checkerboard, std::array<bool, 6> faces /*= SI
// draw the grid behind the layers using a temporary copy // draw the grid behind the layers using a temporary copy
if (use_blend) if (use_blend)
{ {
glEnable(blend_state()); apply_canvas_capability(blend_state(), true);
//draw the grid //draw the grid
if (draw_checkerboard) if (draw_checkerboard)
@@ -1595,8 +1663,8 @@ void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index
App::I->render_task([&] App::I->render_task([&]
{ {
// prepare common states // prepare common states
glViewport(0, 0, m_width, m_height); apply_canvas_viewport(0, 0, m_width, m_height);
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
@@ -2017,7 +2085,7 @@ void Canvas::import_equirectangular_thread(std::string file_path, std::shared_pt
Plane plane; Plane plane;
plane.create<1>(2, 2); plane.create<1>(2, 2);
draw_objects([&](const glm::mat4& camera, const glm::mat4& proj, int i) { draw_objects([&](const glm::mat4& camera, const glm::mat4& proj, int i) {
glDisable(depth_test_state()); apply_canvas_capability(depth_test_state(), false);
tex.update(img.m_data.get() + indices[i] * stride); tex.update(img.m_data.get() + indices[i] * stride);
m_sampler.bind(0); m_sampler.bind(0);
set_active_texture_unit(0); set_active_texture_unit(0);
@@ -2038,7 +2106,7 @@ void Canvas::import_equirectangular_thread(std::string file_path, std::shared_pt
Sphere sphere; Sphere sphere;
sphere.create<64, 64>(2.f); sphere.create<64, 64>(2.f);
draw_objects([&](const glm::mat4& camera, const glm::mat4& proj, int i) { draw_objects([&](const glm::mat4& camera, const glm::mat4& proj, int i) {
glDisable(depth_test_state()); apply_canvas_capability(depth_test_state(), false);
m_sampler.bind(0); m_sampler.bind(0);
set_active_texture_unit(0); set_active_texture_unit(0);
tex.bind(); tex.bind();
@@ -2174,9 +2242,9 @@ void Canvas::export_depth_thread(std::string file_name)
rtt.bindFramebuffer(); rtt.bindFramebuffer();
rtt.clear({ 0, 0, 0, 1 }); rtt.clear({ 0, 0, 0, 1 });
glEnable(blend_state()); apply_canvas_capability(blend_state(), true);
glDisable(depth_test_state()); apply_canvas_capability(depth_test_state(), false);
glViewport(0, 0, rtt.getWidth(), rtt.getHeight()); apply_canvas_viewport(0, 0, rtt.getWidth(), rtt.getHeight());
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
{ {
auto plane_mvp_z = proj * camera * auto plane_mvp_z = proj * camera *
@@ -2209,9 +2277,9 @@ void Canvas::export_depth_thread(std::string file_name)
{ {
rtt.bindFramebuffer(); rtt.bindFramebuffer();
rtt.clear({ 0, 0, 0, 1 }); rtt.clear({ 0, 0, 0, 1 });
glEnable(blend_state()); apply_canvas_capability(blend_state(), true);
glDisable(depth_test_state()); apply_canvas_capability(depth_test_state(), false);
glViewport(0, 0, rtt.getWidth(), rtt.getHeight()); apply_canvas_viewport(0, 0, rtt.getWidth(), rtt.getHeight());
for (int layer_index = 0; layer_index < m_layers.size(); layer_index++) for (int layer_index = 0; layer_index < m_layers.size(); layer_index++)
{ {
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
@@ -2901,7 +2969,7 @@ Image Canvas::thumbnail_generate(int w, int h)
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// prepare common states // prepare common states
glViewport(0, 0, w, h); apply_canvas_viewport(0, 0, w, h);
RTT fb; RTT fb;
fb.create(w, h); fb.create(w, h);
@@ -2919,7 +2987,7 @@ Image Canvas::thumbnail_generate(int w, int h)
fb.clear({ 1, 1, 1, 0 }); fb.clear({ 1, 1, 1, 0 });
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
auto plane_mvp = proj * m_mv * m_plane_transform[i] * glm::translate(glm::vec3(0, 0, -1)); auto plane_mvp = proj * m_mv * m_plane_transform[i] * glm::translate(glm::vec3(0, 0, -1));
ShaderManager::use(kShader::TextureBlend); ShaderManager::use(kShader::TextureBlend);
@@ -2969,7 +3037,7 @@ Image Canvas::thumbnail_generate(int w, int h)
m_face_plane.draw_fill(); m_face_plane.draw_fill();
// now blend with the background // now blend with the background
glEnable(blend_state()); apply_canvas_capability(blend_state(), true);
ShaderManager::use(kShader::Texture); ShaderManager::use(kShader::Texture);
ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f));
@@ -2988,8 +3056,8 @@ Image Canvas::thumbnail_generate(int w, int h)
blendtex.destroy(); blendtex.destroy();
// restore viewport and clear color states // restore viewport and clear color states
blend ? glEnable(blend_state()) : glDisable(blend_state()); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
set_active_texture_unit(0); set_active_texture_unit(0);
}); });
@@ -3036,8 +3104,8 @@ void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, con
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// prepare common states // prepare common states
glViewport(0, 0, layer.w, layer.h); apply_canvas_viewport(0, 0, layer.w, layer.h);
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
GLuint rboID = allocate_canvas_depth_renderbuffer(layer.w, layer.h); GLuint rboID = allocate_canvas_depth_renderbuffer(layer.w, layer.h);
@@ -3060,8 +3128,8 @@ void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, con
delete_canvas_renderbuffer(rboID); delete_canvas_renderbuffer(rboID);
// restore viewport and clear color states // restore viewport and clear color states
blend ? glEnable(blend_state()) : glDisable(blend_state()); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
set_active_texture_unit(0); set_active_texture_unit(0);
}); });
@@ -3079,8 +3147,8 @@ void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm:
auto blend = glIsEnabled(blend_state()); auto blend = glIsEnabled(blend_state());
// prepare common states // prepare common states
glViewport(0, 0, layer.w, layer.h); apply_canvas_viewport(0, 0, layer.w, layer.h);
glDisable(blend_state()); apply_canvas_capability(blend_state(), false);
GLuint rboID = allocate_canvas_depth_renderbuffer(layer.w, layer.h); GLuint rboID = allocate_canvas_depth_renderbuffer(layer.w, layer.h);
@@ -3166,8 +3234,8 @@ void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm:
rtt.destroy(); rtt.destroy();
// restore viewport and clear color states // restore viewport and clear color states
blend ? glEnable(blend_state()) : glDisable(blend_state()); blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_canvas_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
set_active_texture_unit(0); set_active_texture_unit(0);
}); });

View File

@@ -1,8 +1,34 @@
#include "pch.h" #include "pch.h"
#include "hmd.h" #include "hmd.h"
#include "log.h" #include "log.h"
#include "renderer_gl/opengl_capabilities.h"
#include <array> #include <array>
namespace {
void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept
{
glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
void apply_hmd_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
const auto status = pp::renderer::gl::apply_opengl_viewport(
pp::renderer::gl::OpenGlViewportRect {
.x = x,
.y = y,
.width = width,
.height = height,
},
pp::renderer::gl::OpenGlViewportDispatch {
.viewport = set_opengl_viewport,
});
if (!status.ok())
LOG("HMD viewport dispatch failed because: %s", status.message);
}
}
std::map<ViveController::kButton, ViveController::kButtonMask> ViveController::m_mask { std::map<ViveController::kButton, ViveController::kButtonMask> ViveController::m_mask {
{ ViveController::kButton::Trigger, ViveController::kButtonMask::TriggerBit }, { ViveController::kButton::Trigger, ViveController::kButtonMask::TriggerBit },
{ ViveController::kButton::Pad, ViveController::kButtonMask::PadBit }, { ViveController::kButton::Pad, ViveController::kButtonMask::PadBit },
@@ -181,7 +207,7 @@ void Vive::Draw()
{ {
m_eyes[eye].bindFramebuffer(); m_eyes[eye].bindFramebuffer();
m_eyes[eye].clear(); m_eyes[eye].clear();
glViewport(0, 0, m_eyes[eye].getWidth(), m_eyes[eye].getHeight()); apply_hmd_viewport(0, 0, m_eyes[eye].getWidth(), m_eyes[eye].getHeight());
if (on_draw) if (on_draw)
on_draw(m_proj[eye], m_view[eye], m_pose); on_draw(m_proj[eye], m_view[eye], m_pose);

View File

@@ -52,6 +52,50 @@ void unbind_texture_2d()
LOG("NodeCanvas texture unbind dispatch failed because: %s", status.message); LOG("NodeCanvas texture unbind dispatch failed because: %s", status.message);
} }
void enable_opengl_state(std::uint32_t state) noexcept
{
glEnable(static_cast<GLenum>(state));
}
void disable_opengl_state(std::uint32_t state) noexcept
{
glDisable(static_cast<GLenum>(state));
}
void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept
{
glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
void apply_node_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
const auto status = pp::renderer::gl::apply_opengl_viewport(
pp::renderer::gl::OpenGlViewportRect {
.x = x,
.y = y,
.width = width,
.height = height,
},
pp::renderer::gl::OpenGlViewportDispatch {
.viewport = set_opengl_viewport,
});
if (!status.ok())
LOG("NodeCanvas viewport dispatch failed because: %s", status.message);
}
void apply_node_canvas_capability(std::uint32_t state, bool enabled)
{
const auto status = pp::renderer::gl::apply_opengl_capability(
state,
enabled,
pp::renderer::gl::OpenGlCapabilityDispatch {
.enable = enable_opengl_state,
.disable = disable_opengl_state,
});
if (!status.ok())
LOG("NodeCanvas capability dispatch failed because: %s", status.message);
}
pp::renderer::RenderDeviceFeatures node_canvas_stroke_composite_features() noexcept pp::renderer::RenderDeviceFeatures node_canvas_stroke_composite_features() noexcept
{ {
return ShaderManager::render_device_features(); return ShaderManager::render_device_features();
@@ -245,7 +289,7 @@ void NodeCanvas::draw()
auto depth = glIsEnabled(pp::renderer::gl::depth_test_state()); auto depth = glIsEnabled(pp::renderer::gl::depth_test_state());
auto scissor = glIsEnabled(pp::renderer::gl::scissor_test_state()); auto scissor = glIsEnabled(pp::renderer::gl::scissor_test_state());
glDisable(pp::renderer::gl::scissor_test_state()); apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false);
glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w); glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w);
@@ -286,13 +330,13 @@ void NodeCanvas::draw()
m_rtt.bindFramebuffer(); m_rtt.bindFramebuffer();
glClearColor(1, 1, 0, 0); glClearColor(1, 1, 0, 0);
glClear(pp::renderer::gl::framebuffer_color_buffer_mask()); glClear(pp::renderer::gl::framebuffer_color_buffer_mask());
glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); apply_node_canvas_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
} }
else else
{ {
glClearColor(1, 1, 1, 0); glClearColor(1, 1, 1, 0);
glClear(pp::renderer::gl::framebuffer_color_buffer_mask()); glClear(pp::renderer::gl::framebuffer_color_buffer_mask());
glViewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
} }
// NOTE: draw_merge has been disabled for worst performance // NOTE: draw_merge has been disabled for worst performance
@@ -301,7 +345,7 @@ void NodeCanvas::draw()
if (draw_merged) if (draw_merged)
{ {
glDisable(pp::renderer::gl::blend_state()); apply_node_canvas_capability(pp::renderer::gl::blend_state(), false);
// draw the grid // draw the grid
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
{ {
@@ -347,7 +391,7 @@ void NodeCanvas::draw()
if (use_blend) if (use_blend)
{ {
glViewport(0, 0, m_cache_rtt.getWidth(), m_cache_rtt.getHeight()); apply_node_canvas_viewport(0, 0, m_cache_rtt.getWidth(), m_cache_rtt.getHeight());
m_cache_rtt.bindFramebuffer(); m_cache_rtt.bindFramebuffer();
m_cache_rtt.clear({ 1, 1, 1, 0 }); m_cache_rtt.clear({ 1, 1, 1, 0 });
} }
@@ -369,8 +413,8 @@ void NodeCanvas::draw()
} }
// if not using shader blend, use gl rasterizer blend // if not using shader blend, use gl rasterizer blend
use_blend ? glDisable(pp::renderer::gl::blend_state()) : glEnable(pp::renderer::gl::blend_state()); use_blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), false) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
glDisable(pp::renderer::gl::depth_test_state()); apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
const auto& b = m_canvas->m_current_stroke->m_brush; const auto& b = m_canvas->m_current_stroke->m_brush;
@@ -589,15 +633,15 @@ void NodeCanvas::draw()
{ {
m_cache_rtt.unbindFramebuffer(); m_cache_rtt.unbindFramebuffer();
if (m_density != 1.f) if (m_density != 1.f)
glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); apply_node_canvas_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
else else
glViewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
} }
// draw the grid behind the layers using a temporary copy // draw the grid behind the layers using a temporary copy
if (use_blend) if (use_blend)
{ {
glEnable(pp::renderer::gl::blend_state()); apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
//draw the grid //draw the grid
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
@@ -625,7 +669,7 @@ void NodeCanvas::draw()
} }
} }
glDisable(pp::renderer::gl::depth_test_state()); apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
if (m_canvas->m_smask_active || m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut) if (m_canvas->m_smask_active || m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut)
{ {
@@ -641,7 +685,7 @@ void NodeCanvas::draw()
ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_vec2(kShaderUniform::PatternOffset, m_outline_pan); ShaderManager::u_vec2(kShaderUniform::PatternOffset, m_outline_pan);
set_active_texture_unit(0); set_active_texture_unit(0);
glEnable(pp::renderer::gl::blend_state()); apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
//draw the cube faces //draw the cube faces
for (int plane_index = 0; plane_index < 6; plane_index++) for (int plane_index = 0; plane_index < 6; plane_index++)
@@ -676,7 +720,7 @@ void NodeCanvas::draw()
glClearColor(1, 1, 1, 0); glClearColor(1, 1, 1, 0);
glClear(pp::renderer::gl::framebuffer_color_buffer_mask()); glClear(pp::renderer::gl::framebuffer_color_buffer_mask());
glViewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w); apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
// draw the canvas // draw the canvas
m_sampler_nearest.bind(0); m_sampler_nearest.bind(0);
@@ -689,10 +733,10 @@ void NodeCanvas::draw()
m_rtt.unbindTexture(); m_rtt.unbindTexture();
} }
scissor ? glEnable(pp::renderer::gl::scissor_test_state()) : glDisable(pp::renderer::gl::scissor_test_state()); scissor ? apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::scissor_test_state(), false);
blend ? glEnable(pp::renderer::gl::blend_state()) : glDisable(pp::renderer::gl::blend_state()); blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), true) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), false);
depth ? glEnable(pp::renderer::gl::depth_test_state()) : glDisable(pp::renderer::gl::depth_test_state()); depth ? apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), true) : apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_node_canvas_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
} }

View File

@@ -72,6 +72,74 @@ void unbind_texture_2d()
LOG("NodeStrokePreview texture unbind dispatch failed because: %s", status.message); LOG("NodeStrokePreview texture unbind dispatch failed because: %s", status.message);
} }
void enable_opengl_state(std::uint32_t state) noexcept
{
glEnable(static_cast<GLenum>(state));
}
void disable_opengl_state(std::uint32_t state) noexcept
{
glDisable(static_cast<GLenum>(state));
}
void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept
{
glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
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));
}
void apply_stroke_preview_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
const auto status = pp::renderer::gl::apply_opengl_viewport(
pp::renderer::gl::OpenGlViewportRect {
.x = x,
.y = y,
.width = width,
.height = height,
},
pp::renderer::gl::OpenGlViewportDispatch {
.viewport = set_opengl_viewport,
});
if (!status.ok())
LOG("NodeStrokePreview viewport dispatch failed because: %s", status.message);
}
void apply_stroke_preview_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(
pp::renderer::gl::OpenGlScissorRect {
.enabled = 1U,
.x = x,
.y = y,
.width = width,
.height = height,
},
pp::renderer::gl::OpenGlScissorDispatch {
.enable = enable_opengl_state,
.disable = disable_opengl_state,
.scissor = set_opengl_scissor,
});
if (!status.ok())
LOG("NodeStrokePreview scissor dispatch failed because: %s", status.message);
}
void apply_stroke_preview_capability(std::uint32_t state, bool enabled)
{
const auto status = pp::renderer::gl::apply_opengl_capability(
state,
enabled,
pp::renderer::gl::OpenGlCapabilityDispatch {
.enable = enable_opengl_state,
.disable = disable_opengl_state,
});
if (!status.ok())
LOG("NodeStrokePreview capability dispatch failed because: %s", status.message);
}
} }
std::atomic_int NodeStrokePreview::s_instances{ 0 }; std::atomic_int NodeStrokePreview::s_instances{ 0 };
@@ -155,12 +223,12 @@ void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2
m_rtt_mixer.bindFramebuffer(); m_rtt_mixer.bindFramebuffer();
glViewport(0, 0, m_rtt_mixer.getWidth(), m_rtt_mixer.getHeight()); apply_stroke_preview_viewport(0, 0, m_rtt_mixer.getWidth(), m_rtt_mixer.getHeight());
glDisable(pp::renderer::gl::depth_test_state()); apply_stroke_preview_capability(pp::renderer::gl::depth_test_state(), false);
glEnable(pp::renderer::gl::scissor_test_state()); apply_stroke_preview_capability(pp::renderer::gl::scissor_test_state(), true);
glDisable(pp::renderer::gl::blend_state()); apply_stroke_preview_capability(pp::renderer::gl::blend_state(), false);
glScissor( apply_stroke_preview_scissor(
static_cast<int>(bb_min.x), static_cast<int>(bb_min.x),
static_cast<int>(bb_min.y), static_cast<int>(bb_min.y),
static_cast<int>(bb_sz.x), static_cast<int>(bb_sz.x),
@@ -339,7 +407,7 @@ void NodeStrokePreview::draw_stroke_immediate()
glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() }; glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
glm::mat4 ortho_proj = glm::ortho<float>(0, size.x, 0, size.y, -1, 1); glm::mat4 ortho_proj = glm::ortho<float>(0, size.x, 0, size.y, -1, 1);
glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); apply_stroke_preview_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
m_rtt.bindFramebuffer(); m_rtt.bindFramebuffer();
m_rtt.clear(); m_rtt.clear();
m_sampler_mipmap.bind(0); m_sampler_mipmap.bind(0);
@@ -418,7 +486,7 @@ void NodeStrokePreview::draw_stroke_immediate()
if (b->m_pattern_flipx) patt_scale.x *= -1.f; if (b->m_pattern_flipx) patt_scale.x *= -1.f;
if (b->m_pattern_flipy) patt_scale.y *= -1.f; if (b->m_pattern_flipy) patt_scale.y *= -1.f;
glDisable(pp::renderer::gl::blend_state()); apply_stroke_preview_capability(pp::renderer::gl::blend_state(), false);
ShaderManager::use(kShader::Stroke); ShaderManager::use(kShader::Stroke);
ShaderManager::u_int(kShaderUniform::Tex, 0); // brush ShaderManager::u_int(kShaderUniform::Tex, 0); // brush
const auto stroke_feedback = stroke_preview_destination_feedback_plan(m_rtt.getWidth(), m_rtt.getHeight()); const auto stroke_feedback = stroke_preview_destination_feedback_plan(m_rtt.getWidth(), m_rtt.getHeight());
@@ -600,7 +668,7 @@ void NodeStrokePreview::draw_stroke_immediate()
m_rtt.unbindFramebuffer(); m_rtt.unbindFramebuffer();
glViewport(vp[0], vp[1], vp[2], vp[3]); apply_stroke_preview_viewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
} }