881 lines
40 KiB
Markdown
881 lines
40 KiB
Markdown
# PanoPainter Modernization Roadmap
|
|
|
|
Status: live
|
|
Last updated: 2026-06-02
|
|
|
|
This is the living roadmap for modernizing PanoPainter into independently
|
|
testable C++23 components while retaining all existing functionality. Keep this
|
|
file current as phases are implemented. Do not let shortcuts, skipped platforms,
|
|
or temporary adapters live only in chat history.
|
|
|
|
## How To Keep This Roadmap Live
|
|
|
|
- Update the phase status before and after each implementation pass.
|
|
- When a shortcut is introduced, add it to the debt log section in this file
|
|
until `docs/modernization/debt.md` exists, then move debt entries there.
|
|
- When a major architectural decision is made, add an ADR under `docs/adr/`
|
|
once that directory exists.
|
|
- Every phase must preserve old behavior unless the roadmap explicitly says
|
|
otherwise.
|
|
- Each phase must leave the repo in a buildable and testable state.
|
|
- Do not add stubs without a debt entry, validation command, and removal
|
|
condition.
|
|
|
|
## Locked Decisions
|
|
|
|
- Graphics path: keep OpenGL working first; add Vulkan and Metal after the
|
|
renderer boundary exists.
|
|
- Required platforms at phase gates: Windows desktop/AppX, macOS, iOS,
|
|
Android standard, Quest, Focus/Wave, Linux, and WebGL.
|
|
- Dependency policy: use vcpkg where reliable; keep SDK, patched, or
|
|
vendor-only dependencies with documented reasons.
|
|
- Test stack: Catch2, golden/approval tests, and fuzz/property tests where
|
|
useful.
|
|
- Automation: local reproducible matrix first; hosted CI can be added later.
|
|
- Documentation: ADRs, debt log, and this living roadmap.
|
|
- "vkpkg" in older notes means `vcpkg`.
|
|
- Target C++ standard: C++23.
|
|
- Initial Windows CMake generator target: Visual Studio 2026 when available.
|
|
|
|
## Phase Status
|
|
|
|
| Phase | Name | Status | Gate |
|
|
| --- | --- | --- | --- |
|
|
| 0 | Inventory, Safety Rails, And Memory | Complete | No behavior changes; old builds still work |
|
|
| 1 | Unified CMake Skeleton | In progress | Root CMake builds the Windows app and owns the source list |
|
|
| 2 | Toolchain, Diagnostics, And Dependencies | In progress | Strict desktop library builds compile cleanly |
|
|
| 3 | Test Harness And Agent-Ready Automation | In progress | `ctest --preset desktop-fast` runs headlessly |
|
|
| 4 | Component Split Without Behavior Change | Started | Each extracted target builds and tests |
|
|
| 5 | Renderer Boundary And OpenGL Parity | Started | OpenGL output matches golden readbacks |
|
|
| 6 | Platform Alignment | Not started | Every supported platform has named validation |
|
|
| 7 | Hardening, Coverage, And Breaking-Point Tests | Not started | Each component has edge/failure tests |
|
|
| 8 | Future Backend Readiness | Not started | Vulkan/Metal lab targets remain non-default |
|
|
|
|
## Target Component Architecture
|
|
|
|
The refactor should move toward one-way dependencies:
|
|
|
|
```text
|
|
pp_foundation
|
|
-> pp_assets
|
|
-> pp_paint
|
|
-> pp_document
|
|
-> pp_renderer_api
|
|
-> pp_renderer_gl
|
|
-> pp_paint_renderer
|
|
-> pp_ui_core
|
|
-> pp_panopainter_ui
|
|
-> pp_platform_*
|
|
-> panopainter_app
|
|
```
|
|
|
|
Intended responsibilities:
|
|
|
|
- `pp_foundation`: logging facade, math/util helpers, events, task queues,
|
|
binary streams.
|
|
- `pp_assets`: `Asset`, `Image`, `Settings`, serialization, ABR, PPBR, and PPI
|
|
helpers.
|
|
- `pp_paint`: pure `Brush`, `Stroke`, stroke sampling, and CPU reference blend
|
|
math.
|
|
- `pp_document`: canvas document model, layers, animation frames, and undo/redo
|
|
model.
|
|
- `pp_renderer_api`: renderer-neutral interfaces for textures, render targets,
|
|
shaders, meshes, readback, frame capture, and tracing.
|
|
- `pp_renderer_gl`: current OpenGL implementation behind renderer interfaces.
|
|
- `pp_paint_renderer`: stroke rasterization, layer compositing, cube/equirect
|
|
export using `pp_renderer_api`.
|
|
- `pp_ui_core`: `Node`, layout, generic controls, text/image primitives.
|
|
- `pp_panopainter_ui`: panels, dialogs, `NodeCanvas`, and app-specific
|
|
workflows.
|
|
- `pp_platform_*`: Windows, macOS/iOS, Android, Linux, and WebGL shells.
|
|
- `panopainter_app`: composition root only.
|
|
|
|
Rules:
|
|
|
|
- Component headers must not include platform SDK or graphics API headers unless
|
|
the component name includes that backend or platform.
|
|
- Pure libraries must build and test without a window, GL context, network,
|
|
tablet, VR headset, or filesystem outside test temp directories.
|
|
- Public APIs should return explicit status/result objects. PanoPainter app
|
|
code keeps exceptions disabled unless isolated SDK wrappers require them.
|
|
- Singleton access should be replaced at component boundaries with context or
|
|
service objects. Temporary facade shims require debt entries.
|
|
|
|
## Phase 0: Inventory, Safety Rails, And Memory
|
|
|
|
Status: complete on 2026-05-31. Created this roadmap,
|
|
`docs/modernization/debt.md`, `docs/modernization/capability-map.md`,
|
|
`docs/modernization/build-inventory.md`, and ADR 0001.
|
|
|
|
Goal: create durable project memory and prevent silent shortcuts before large
|
|
refactors begin.
|
|
|
|
Implementation tasks:
|
|
|
|
- Add `docs/modernization/roadmap.md`, `docs/modernization/debt.md`, and
|
|
`docs/adr/`.
|
|
- Add a shortcut rule: every temporary adapter, fallback, skipped platform, or
|
|
retained vendored dependency must have owner, reason, validation command, and
|
|
removal condition.
|
|
- Generate a current capability map covering:
|
|
- project open/save and PPI compatibility
|
|
- image import/export and thumbnails
|
|
- brush presets, ABR import, PPBR export/import
|
|
- layers, blend modes, alpha lock, selection mask
|
|
- animation frames and MP4/timelapse recording
|
|
- VR, tablet, touch, mouse, keyboard, gestures
|
|
- cloud upload/download/browse
|
|
- UI dialogs, panels, layout XML, settings
|
|
- Windows/AppX, macOS, iOS, Android standard, Quest, Focus/Wave, Linux, WebGL
|
|
- Record current build commands and known platform prerequisites.
|
|
|
|
Gate:
|
|
|
|
- No behavior changes.
|
|
- Existing Visual Studio, platform CMake, Gradle, Apple, Linux, and WebGL paths
|
|
are not removed.
|
|
|
|
## Phase 1: Unified CMake Skeleton
|
|
|
|
Goal: make CMake the canonical source list without breaking existing projects.
|
|
|
|
Status: in progress. Root `CMakeLists.txt`, `CMakePresets.json`, and project
|
|
option targets exist. The Windows desktop app builds through CMake as
|
|
`PanoPainter`; the raw Visual Studio solution/project files were removed on
|
|
2026-05-31 by user decision. Android arm64 now configures and builds headless
|
|
foundation/tool targets through the root CMake/NDK path. Non-Windows platform
|
|
app/package files remain during Phase 6 alignment.
|
|
|
|
Implementation tasks:
|
|
|
|
- Add root `CMakeLists.txt` and shared CMake modules under `cmake/`.
|
|
- Add `CMakePresets.json` with at least:
|
|
- `windows-vs2026-x64`
|
|
- `windows-clangcl-asan`
|
|
- `linux-clang`
|
|
- `android-arm64`
|
|
- `android-x64`
|
|
- `emscripten`
|
|
- `macos`
|
|
- `ios-device`
|
|
- `ios-simulator`
|
|
- Keep Android CMake, Linux CMake, WebGL CMake, Apple project files, and AppX
|
|
packaging during the transition until each consumes shared component targets.
|
|
- Move version generation into a CMake custom command using
|
|
`scripts/pre-build.py`.
|
|
- Fix `scripts/pre-build.py` only if required to avoid unnecessary rewrites or
|
|
missing-tag failures.
|
|
- Add CMake options:
|
|
- `PP_BUILD_APP`
|
|
- `PP_BUILD_TESTS`
|
|
- `PP_BUILD_TOOLS`
|
|
- `PP_ENABLE_OPENGL`
|
|
- `PP_ENABLE_VULKAN_EXPERIMENTAL=OFF`
|
|
- `PP_ENABLE_VR`
|
|
- `PP_ENABLE_CLOUD`
|
|
- `PP_ENABLE_VIDEO`
|
|
- Define source-list helper targets so per-platform source duplication can be
|
|
reduced incrementally.
|
|
|
|
Gate:
|
|
|
|
- Windows desktop app builds through CMake.
|
|
- New CMake can configure on Windows.
|
|
- Source list differences are understood and documented.
|
|
- Non-Windows platform migration is debt-tracked until Phase 6.
|
|
|
|
## Phase 2: Toolchain, Diagnostics, And Dependencies
|
|
|
|
Goal: turn the build into an error-finding system before deep refactors.
|
|
|
|
Status: in progress. Initial warning/sanitizer option targets, `vcpkg.json`,
|
|
a validated Windows headless vcpkg preset, `pp_ui_core` support for vcpkg
|
|
tinyxml2 on that preset, and a headless `panopainter_validate_shaders` target
|
|
exist. `windows-clangcl-asan` now configures as a headless Ninja/clang-cl ASan
|
|
preset and uses the release MSVC runtime required by clang-cl ASan, but local
|
|
ASan builds are blocked by DEBT-0014 until Clang and the selected MSVC STL are
|
|
compatible. Dependency migration is not complete until remaining component
|
|
dependencies and mobile/Apple triplets are validated.
|
|
|
|
Implementation tasks:
|
|
|
|
- Set C++23 through target features, not raw compiler flags.
|
|
- Add warning profiles:
|
|
- MSVC: `/W4 /permissive- /Zc:__cplusplus /Zc:preprocessor`.
|
|
- Optional MSVC analysis preset: `/analyze`.
|
|
- Clang/GCC: `-Wall -Wextra -Wpedantic -Wconversion -Wshadow
|
|
-Wnull-dereference`.
|
|
- Keep exceptions disabled for PanoPainter targets, except isolated SDK wrapper
|
|
targets when unavoidable.
|
|
- Add sanitizer presets:
|
|
- Clang/GCC ASan and UBSan for headless libraries.
|
|
- MSVC ASan where supported.
|
|
- TSan only for pure/headless targets.
|
|
- Add tooling hooks:
|
|
- `clang-tidy`
|
|
- `cppcheck`
|
|
- shader validation or compile checks
|
|
- CTest dashboard output
|
|
- Add `vcpkg.json`.
|
|
- Move reliable dependencies to vcpkg first:
|
|
- `fmt`
|
|
- `glm`
|
|
- `tinyxml2`
|
|
- `stb`
|
|
- `curl`
|
|
- `sqlite3`
|
|
- `glad`
|
|
- `Catch2`
|
|
- Keep vendored until proven:
|
|
- OpenVR
|
|
- OVR/Wave SDKs
|
|
- Wacom WinTab
|
|
- AppCenter
|
|
- openh264
|
|
- mp4v2
|
|
- libyuv
|
|
- patched or SDK-specific libraries
|
|
|
|
Gate:
|
|
|
|
- Desktop library targets compile with strict diagnostics.
|
|
- New warnings caused by refactor are fixed or locally justified.
|
|
- No global blanket warning suppression for project code.
|
|
|
|
## Phase 3: Test Harness And Agent-Ready Automation
|
|
|
|
Goal: make each component reachable by automated tools and future agents.
|
|
|
|
Status: in progress. `tests/` exists, `desktop-fast` runs headlessly, and
|
|
PowerShell/bash wrappers exist for
|
|
configure/build/test/analyze/platform-build/package-smoke. `pano_cli` exists
|
|
with JSON automation commands for creating a `pp_document` model, metadata-only
|
|
PPI project loading, and inspecting image signatures, PPI headers, and layout
|
|
XML; full document/app integration is debt-tracked as DEBT-0010 and full PPI
|
|
body parsing is debt-tracked as DEBT-0013.
|
|
|
|
Implementation tasks:
|
|
|
|
- Add `tests/` with one executable per component.
|
|
- Register CTest labels:
|
|
- `foundation`
|
|
- `assets`
|
|
- `paint`
|
|
- `document`
|
|
- `renderer`
|
|
- `ui`
|
|
- `platform`
|
|
- `integration`
|
|
- `fuzz`
|
|
- `slow`
|
|
- `gpu`
|
|
- Add `tools/pano_cli` for headless automation.
|
|
- `pano_cli` should support:
|
|
- create document
|
|
- load project
|
|
- save project
|
|
- apply scripted strokes
|
|
- import/export images
|
|
- inspect layers
|
|
- run layout parse
|
|
- emit JSON results
|
|
- Add local automation wrappers under `scripts/automation/`:
|
|
- configure
|
|
- build
|
|
- test
|
|
- analyze
|
|
- package smoke
|
|
- All wrappers must return machine-readable logs or summaries.
|
|
- Establish `tests/data/` fixtures:
|
|
- tiny PPI files
|
|
- corrupt/truncated PPI cases
|
|
- PNG/JPEG fixtures
|
|
- ABR/PPBR samples
|
|
- layout XML
|
|
- shader snippets
|
|
- brush stroke scripts
|
|
|
|
Gate:
|
|
|
|
- `ctest --preset desktop-fast --build-config Debug` runs without a GL
|
|
context.
|
|
- Non-render components can be tested on a headless machine.
|
|
|
|
## Phase 4: Component Split Without Behavior Change
|
|
|
|
Goal: split libraries while keeping current app behavior.
|
|
|
|
Status: started. `pp_foundation` exists with binary stream utilities and
|
|
boundary/overread tests. It also owns strict decimal `uint32` parsing used by
|
|
`pano_cli`, with rejection tests for empty, signed, mixed, and overflowing
|
|
input. A synchronous event dispatcher, structured logging facade, bounded FIFO
|
|
task queue, and deterministic `TraceRecorder` now record
|
|
component/name/thread/frame/stroke metadata with filtering, capacity, and
|
|
invalid-end tests. `pp_assets` has started with PNG/JPEG signature detection,
|
|
PNG IHDR metadata parsing, PPI header/project byte-layout/body-summary
|
|
recognition, layer/frame indexing, dirty-face PNG payload metadata validation,
|
|
asset-level RGBA PNG payload decoding, and a pure typed settings document
|
|
model, with
|
|
corrupt/truncated/unsupported, extreme-dimension, and key/value limit tests.
|
|
`pp_paint` has started with pure brush parameter validation/stamp evaluation,
|
|
CPU reference math for the five current shader blend modes, and deterministic
|
|
stroke spacing/interpolation plus a pure text stroke-script parser.
|
|
`pp_document` has
|
|
started with a pure canvas/layer/frame model, alpha-lock metadata, snapshot
|
|
construction, per-layer frame metadata, layer metadata operations, frame
|
|
move/duration queries, renderer-free RGBA8 cube-face payload storage,
|
|
renderer-free alpha8 selection-mask storage, PPI image import, and
|
|
layer/frame/undo-redo history invariant tests.
|
|
`pp_renderer_api` has started with renderer-neutral
|
|
texture/readback descriptors and validation tests. `pp_paint_renderer` has
|
|
started with deterministic CPU layer compositing over renderer extents using
|
|
the paint blend reference. `pp_ui_core` has started with XML-layout-facing
|
|
length parsing, color parsing, tinyxml-backed layout XML parsing, and invalid
|
|
input tests.
|
|
`pano_cli inspect-image` exposes PNG IHDR metadata as JSON,
|
|
`pano_cli import-image` accepts a PNG path and imports decoded RGBA8 pixels
|
|
into a new pure `pp_document` face payload,
|
|
with checked-in decodable PNG and truncated PNG automation coverage,
|
|
`pano_cli export-image` writes a deterministic RGBA8 PNG through `pp_assets`
|
|
and round-trips it back through file import automation,
|
|
`pano_cli inspect-project` reports validated PPI thumbnail/body byte layout,
|
|
body summary, layer/frame descriptors, dirty-face PNG payload metadata, and
|
|
asset-level decode coverage, and
|
|
`pano_cli load-project` creates a `pp_document` projection with per-layer frame
|
|
counts, durations, and decoded face-pixel payload attachment when PPI image
|
|
payloads are present.
|
|
`pano_cli save-project` writes generated multi-layer, multi-frame PPI files
|
|
with layer opacity, blend mode, alpha lock, and visibility metadata through the
|
|
extracted `pp_assets` writer and round-trips metadata-only and test
|
|
dirty-face-payload variants through `load-project`.
|
|
`pano_cli create-document` can create simple animation documents with explicit
|
|
frame count/duration. `pano_cli simulate-document-edits` exercises pure
|
|
layer metadata, frame reordering, active-index preservation, tiny face-payload
|
|
attachment, and selection-mask attachment. `pano_cli simulate-document-history` exercises the
|
|
pure `pp_document::DocumentHistory` apply/undo/redo path and emits JSON state
|
|
summaries. `pano_cli simulate-image-import` decodes an embedded tiny PNG
|
|
through `pp_assets` and attaches the resulting RGBA8 payload to `pp_document`.
|
|
`pano_cli simulate-stroke` exercises the pure stroke sampler for
|
|
scripted-stroke automation. `pano_cli simulate-stroke-script`
|
|
loads stroke script fixtures, parses them through `pp_paint`, and samples every
|
|
stroke. `pano_cli parse-layout` exercises the XML layout path. Continue
|
|
expanding document behavior toward legacy Canvas parity and then port OpenGL
|
|
classes behind the renderer boundary.
|
|
|
|
Implementation tasks:
|
|
|
|
- Extract components in this order:
|
|
1. `pp_foundation`
|
|
2. `pp_assets`
|
|
3. `pp_paint`
|
|
4. `pp_document`
|
|
5. `pp_renderer_api`
|
|
6. `pp_renderer_gl`
|
|
7. `pp_paint_renderer`
|
|
8. `pp_ui_core`
|
|
9. `pp_panopainter_ui`
|
|
10. `pp_platform_*`
|
|
11. `panopainter_app`
|
|
- Remove renderer/platform dependencies from pure headers first, especially:
|
|
- `Brush`
|
|
- document/layer model
|
|
- serializer
|
|
- UI core headers
|
|
- Keep facade shims where needed, but debt-track every shim.
|
|
- Avoid large behavioral rewrites during extraction.
|
|
- Each extracted component gets a focused test suite before moving to the next.
|
|
|
|
Gate:
|
|
|
|
- Old app still launches.
|
|
- Component tests pass after every extraction.
|
|
- No undocumented stubs or shortcuts.
|
|
|
|
## Phase 5: Renderer Boundary And OpenGL Parity
|
|
|
|
Goal: make OpenGL an implementation detail and establish parity tests before
|
|
adding new backends.
|
|
|
|
Status: started. `pp_renderer_api` exists as a headless renderer-neutral target
|
|
with texture descriptor, byte-size, viewport, mesh, readback bounds, command
|
|
context, render device, shader program descriptor, mesh, render target,
|
|
readback, trace interface validation, and the canonical PanoPainter shader
|
|
catalog now consumed by the legacy OpenGL app initialization path.
|
|
`pp_renderer_gl` now exists as the first OpenGL backend library and owns pure
|
|
OpenGL capability detection for framebuffer fetch, map-buffer alignment, and
|
|
float texture support. It also owns the OpenGL texture upload-type mapping used
|
|
by legacy `Texture2D` and `RTT` creation, RGBA pixel-format mapping used by
|
|
`RTT` texture allocation, plus image channel-count to texture
|
|
format mapping for `Texture2D` image uploads and framebuffer status naming for
|
|
`RTT` and `Texture2D` diagnostics. `Texture2D` 2D texture binding, upload,
|
|
mipmap generation, framebuffer readback setup, and update component-type tokens
|
|
now delegate to `pp_renderer_gl`. `TextureCube` cube-map binding, allocation
|
|
face targets, RGBA allocation format, and unsigned-byte component type also
|
|
delegate to `pp_renderer_gl`. RGBA8/RGBA32F readback formats, byte-count math, and PBO
|
|
pixel-buffer target/usage/access tokens used by `RTT` and `PBO` readbacks now
|
|
live in `pp_renderer_gl`. The framebuffer blit color mask and linear/nearest
|
|
filter tokens used by `RTT::resize` and `RTT::copy`, plus the default
|
|
render-target texture parameters, texture/renderbuffer targets, depth format,
|
|
framebuffer targets, binding queries, attachment points, and completion status
|
|
used by `RTT::create` and framebuffer bind/restore paths, also live in
|
|
`pp_renderer_gl`. RTT clear color/depth masks and color-write-mask query tokens
|
|
also live in `pp_renderer_gl`. `RTT` no longer spells GL enum names directly.
|
|
Mesh index-type and primitive-mode
|
|
decisions used by legacy `Shape` drawing, plus Shape buffer targets, static
|
|
upload usage, and vertex attribute component/normalization tokens, also live
|
|
in `pp_renderer_gl`. The PanoPainter cube-face to
|
|
OpenGL texture-target mapping used by `TextureCube` also lives in
|
|
`pp_renderer_gl`. The legacy app delegates extension, upload-format,
|
|
framebuffer diagnostic, framebuffer blit, render-target setup, clear-state,
|
|
2D/cube texture setup, mesh draw-mode, and cube-face texture-target
|
|
interpretation to that backend library. Sampler wrap, min/mag filter, and desktop border-color parameter
|
|
mapping for legacy `Sampler` also lives in `pp_renderer_gl`. The PanoPainter shader attribute
|
|
binding catalog, shader stage tokens, compile/link status queries, active-uniform
|
|
count query, and matrix-uniform transpose token also live in `pp_renderer_gl`
|
|
and are consumed by legacy `Shader` creation. Shader uniform hashing, catalog
|
|
validation, active-uniform mapping, and the legacy uniform uniqueness check now
|
|
delegate to `pp_renderer_gl` as well. `Shader` no longer spells GL enum names
|
|
directly. App OpenGL initialization debug severity, debug output, GL info
|
|
string, default depth/program-point/line-smooth state, blend factor/equation,
|
|
and UI render-target RGBA8 format tokens are now also cataloged and tested in
|
|
`pp_renderer_gl`; the legacy convert command and resize path consume the same
|
|
backend-owned mapping. App clear color-buffer masks, default framebuffer
|
|
binding, scissor state, and sampler filter/wrap tokens now share that backend
|
|
mapping too. OpenGL extension enumeration query tokens used before runtime
|
|
capability detection also live in `pp_renderer_gl`. Legacy font atlas texture
|
|
formats, text mesh buffer targets, attribute component/normalization, draw
|
|
primitive/index type, upload usage, and active texture unit selection also
|
|
delegate to `pp_renderer_gl`; `Font` no longer spells GL enum names directly.
|
|
Canvas undo/redo dirty-region texture updates and readbacks now also delegate
|
|
their 2D texture target, RGBA pixel format, and unsigned-byte component type
|
|
mapping to `pp_renderer_gl`.
|
|
`NodeViewport` preview rendering now also delegates viewport query,
|
|
clear-color query, color-buffer clear mask, and blend-state tokens to
|
|
`pp_renderer_gl`.
|
|
`NodeImageTexture` preview drawing now delegates its fallback 2D texture bind
|
|
target and blend-state tokens to `pp_renderer_gl`.
|
|
`NodeImage` drawing and remote-image texture creation now delegate mipmapped
|
|
sampler filters, blend-state tokens, and RGBA8/RGBA texture format mapping to
|
|
`pp_renderer_gl`.
|
|
`NodeColorWheel` triangle-buffer setup and draw-state handling now delegate
|
|
array-buffer, static-upload, vertex-attribute, primitive-mode, and blend-state
|
|
tokens to `pp_renderer_gl`.
|
|
Simple UI text, text-input, border, scroll, and animation timeline draw paths
|
|
now also delegate blend-state tokens to `pp_renderer_gl`.
|
|
Canvas layer cube/equirect generation, clear, restore, and snapshot paths now
|
|
also delegate cube/2D texture targets, active texture units, blend/clear state,
|
|
and RGBA8 read/write pixel mapping to `pp_renderer_gl`.
|
|
`NodePanelGrid` heightmap preview and lightmap baking now delegate texture
|
|
readback formats, sampler filters, depth/blend state, depth clears, viewport
|
|
queries, color-mask booleans, active texture units, and float render-target
|
|
formats to `pp_renderer_gl`.
|
|
Legacy `util.cpp` OpenGL error naming and `gl_state` save/restore now delegate
|
|
error codes, state queries, framebuffer targets, texture binding targets, and
|
|
active texture units to `pp_renderer_gl`.
|
|
`NodeStrokePreview` brush preview rendering now delegates depth/scissor/blend
|
|
state, viewport/clear-color queries, active texture units, 2D texture targets,
|
|
copy targets, and sampler filters/wraps to `pp_renderer_gl`.
|
|
Legacy `Texture2D`, `TextureManager`, `Sampler`, and `RTT` public headers no
|
|
longer expose raw OpenGL enum defaults; default texture formats, sampler
|
|
filters/wraps, and render-target formats are resolved through backend-owned
|
|
overloads.
|
|
The Windows entrypoint now delegates generic OpenGL error-code/info-string
|
|
tokens and WGL core-context/pixel-format attribute catalogs to `pp_renderer_gl`.
|
|
The existing renderer classes are not yet fully
|
|
behind the renderer interfaces.
|
|
|
|
Implementation tasks:
|
|
|
|
- Introduce renderer interfaces:
|
|
- `IRenderDevice`
|
|
- `ITexture2D`
|
|
- `IRenderTarget`
|
|
- `IShaderProgram`
|
|
- `IMesh`
|
|
- `ICommandContext`
|
|
- `IReadbackBuffer`
|
|
- `IRenderTrace`
|
|
- Port current renderer classes behind OpenGL backend types:
|
|
- `RTT`
|
|
- `Texture2D`
|
|
- `Sampler`
|
|
- `ShaderManager`
|
|
- `Shape`
|
|
- Keep OpenGL runtime capability decisions in `pp_renderer_gl` with headless
|
|
tests before moving GL object lifetimes behind backend types.
|
|
- Preserve current shader behavior and asset paths.
|
|
- Add deterministic GPU tests:
|
|
- clear
|
|
- blit
|
|
- texture upload/download
|
|
- stroke composite
|
|
- erase
|
|
- layer blend
|
|
- equirect export
|
|
- readback bounds
|
|
- Add CPU reference tests for blend modes.
|
|
- Compare GPU output to golden/reference data with explicit tolerances.
|
|
|
|
Gate:
|
|
|
|
- OpenGL readbacks match golden data on Windows and Linux.
|
|
- Mobile/WebGL compile gates remain green.
|
|
|
|
## Phase 6: Platform Alignment
|
|
|
|
Goal: every supported platform consumes the same component targets.
|
|
|
|
Implementation tasks:
|
|
|
|
- Convert these builds to shared component targets:
|
|
- Windows desktop
|
|
- Windows AppX
|
|
- macOS
|
|
- iOS
|
|
- Android standard
|
|
- Android Quest
|
|
- Android Focus/Wave
|
|
- Linux
|
|
- WebGL/Emscripten
|
|
- Keep platform entrypoints thin:
|
|
- window lifecycle
|
|
- input dispatch
|
|
- clipboard
|
|
- file picker/share
|
|
- GL context creation
|
|
- VR SDK bridge
|
|
- packaging only
|
|
- Add or refine CMake toolchain/preset support for:
|
|
- Android NDK ABIs
|
|
- iOS device
|
|
- iOS simulator
|
|
- macOS
|
|
- Emscripten
|
|
- Keep SDK-only imported libraries documented until vcpkg triplets are proven.
|
|
|
|
Gate:
|
|
|
|
- Every platform has a named configure/build command.
|
|
- Missing local prerequisites are documented.
|
|
- Each platform has at least compile or package validation.
|
|
|
|
## Phase 7: Hardening, Coverage, And Breaking-Point Tests
|
|
|
|
Goal: tests should try to break components, not only confirm current happy
|
|
paths.
|
|
|
|
Implementation tasks:
|
|
|
|
- Add property/fuzz tests for:
|
|
- binary streams
|
|
- serializers
|
|
- PPI parsing
|
|
- ABR parsing
|
|
- layout XML parsing
|
|
- image metadata parsing
|
|
- brush parameter extremes
|
|
- layer/frame operations
|
|
- undo/redo invariants
|
|
- Add stress tests for:
|
|
- thousands of stroke samples
|
|
- extreme resolutions guarded by memory limits
|
|
- rapid layer/frame edits
|
|
- corrupt assets
|
|
- cancellation during export
|
|
- concurrent render/UI task scheduling
|
|
- Add coverage for headless libraries on Clang/GCC.
|
|
- Require coverage reports for changed components first; do not set a global
|
|
threshold until the baseline is meaningful.
|
|
- Add tracing spans around:
|
|
- project load/save
|
|
- render passes
|
|
- stroke commit
|
|
- readback
|
|
- export
|
|
- UI layout
|
|
- platform I/O
|
|
- Logs must include component, thread, frame/stroke id, and timing.
|
|
|
|
Gate:
|
|
|
|
- No shortcut remains undocumented.
|
|
- Every component has unit tests and at least one failure or edge test.
|
|
|
|
## Phase 8: Future Backend Readiness
|
|
|
|
Goal: prepare Vulkan and Metal without destabilizing the OpenGL parity path.
|
|
|
|
Implementation tasks:
|
|
|
|
- Create non-default targets only after OpenGL backend parity:
|
|
- `pp_renderer_vulkan_lab`
|
|
- `pp_renderer_metal_lab`
|
|
- Use `D:\Dev\vkpaint` as reference material for Vulkan painting experiments,
|
|
not as direct production code.
|
|
- Before integration, prove:
|
|
- ping-pong compositing path
|
|
- input-attachment/subpass path where applicable
|
|
- feedback-loop or framebuffer-fetch-style path where supported
|
|
- synchronization and layout correctness under validation layers
|
|
- Keep WebGPU as an optional future portability backend, not the core renderer
|
|
contract.
|
|
|
|
Gate:
|
|
|
|
- Vulkan/Metal lab targets are opt-in.
|
|
- OpenGL production backend remains stable.
|
|
|
|
## Test Matrix
|
|
|
|
| Preset/Label | Purpose | Requires |
|
|
| --- | --- | --- |
|
|
| `desktop-fast` | Pure component unit tests | No GPU/window |
|
|
| `desktop-gpu` | OpenGL backend golden/readback tests | GPU/GL context |
|
|
| `fuzz` | Parser and serializer fuzzing | Fuzzer-capable compiler |
|
|
| `stress` | Large and adversarial scenarios | Longer runtime |
|
|
| `platform-build` | Configure/build each supported platform | Local toolchains |
|
|
| `package-smoke` | AppX/APK/Apple/WebGL package smoke | Platform SDKs |
|
|
|
|
Acceptance for each phase:
|
|
|
|
- Previous phase tests still pass.
|
|
- New component has its own tests.
|
|
- No undocumented stubs.
|
|
- No skipped platform without a debt entry.
|
|
- Automation command is recorded in this roadmap or linked docs.
|
|
|
|
## Verified Commands
|
|
|
|
Last verified on 2026-06-02:
|
|
|
|
```powershell
|
|
cmake --preset windows-msvc-default
|
|
cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_document_tests pp_document_ppi_import_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pano_cli PanoPainter
|
|
ctest --preset desktop-fast --build-config Debug
|
|
powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug
|
|
powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli
|
|
cmake --build --preset windows-msvc-default --target panopainter_validate_shaders
|
|
powershell -ExecutionPolicy Bypass -File scripts\automation\analyze.ps1 -Preset windows-msvc-default -NoApp
|
|
set VCPKG_ROOT=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg
|
|
cmake --preset windows-msvc-vcpkg-headless
|
|
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets windows-msvc-vcpkg-headless
|
|
ctest --preset desktop-fast-vcpkg --build-config Debug
|
|
cmake --preset android-arm64
|
|
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64
|
|
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug
|
|
cmake --fresh --preset windows-clangcl-asan
|
|
```
|
|
|
|
Results:
|
|
|
|
- `pp_foundation_binary_stream_tests` passed.
|
|
- `pp_foundation_event_tests` passed.
|
|
- `pp_foundation_log_tests` passed.
|
|
- `pp_foundation_parse_tests` passed.
|
|
- `pp_foundation_task_queue_tests` passed.
|
|
- `pp_foundation_trace_tests` passed.
|
|
- `pp_assets_image_format_tests` passed.
|
|
- `pp_assets_image_metadata_tests` passed.
|
|
- `pp_assets_image_pixels_tests` passed, including RGBA8 PNG decode and corrupt
|
|
payload rejection.
|
|
- `pp_assets_ppi_header_tests` passed, including PPI thumbnail/body layout,
|
|
body summary validation, layer/frame indexing, dirty-face PNG payload
|
|
metadata validation, and decoded dirty-face payload coverage.
|
|
- `pp_assets_settings_document_tests` passed.
|
|
- `pp_paint_brush_tests` passed.
|
|
- `pp_paint_blend_tests` passed.
|
|
- `pp_paint_stroke_tests` passed.
|
|
- `pp_paint_stroke_script_tests` passed.
|
|
- `pp_document_tests` passed, including snapshot construction, alpha-lock
|
|
metadata, per-layer frame metadata, frame move, duration, face-pixel payload
|
|
storage/replacement/rejection, and history invariants.
|
|
- `pp_document_ppi_import_tests` passed, including decoded PPI dirty-face
|
|
payload attachment to `pp_document` layer/frame storage and out-of-range
|
|
payload rejection.
|
|
- `pp_renderer_api_tests` passed, including shader descriptor validation,
|
|
PanoPainter shader catalog validation, and invalid catalog rejection.
|
|
- `pp_paint_renderer_compositor_tests` passed.
|
|
- `pp_ui_core_color_tests` passed.
|
|
- `pp_ui_core_layout_value_tests` passed.
|
|
- `pp_ui_core_layout_xml_tests` passed.
|
|
- `pano_cli_create_document_smoke` passed.
|
|
- `pano_cli_create_animation_document_smoke` passed and reports animation
|
|
duration JSON.
|
|
- `pano_cli_simulate_document_edits_smoke` passed and reports pure
|
|
`pp_document` layer metadata, frame order, active indices, and face-payload
|
|
state as JSON.
|
|
- `pano_cli_simulate_document_history_smoke` passed and reports real
|
|
`pp_document::DocumentHistory` apply/undo/redo state as JSON.
|
|
- `pano_cli_simulate_image_import_smoke` passed and reports embedded PNG decode
|
|
plus `pp_document` face-payload attachment state as JSON.
|
|
- `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure
|
|
test.
|
|
- `pano_cli_inspect_png_metadata_smoke` passed and reports PNG metadata JSON
|
|
for the tiny IHDR fixture.
|
|
- `pano_cli_import_image_rejects_truncated_png` passed as an expected failure
|
|
test, proving the file-driven image import command rejects a metadata-valid
|
|
but undecodable PNG payload.
|
|
- `pano_cli_inspect_project_layout_smoke` passed and reports PPI
|
|
thumbnail/body byte layout, body summary, layer/frame descriptors, and
|
|
dirty-face PNG payload metadata JSON.
|
|
- `pano_cli_load_project_metadata_smoke` passed and reports a `pp_document`
|
|
projection with per-layer frame counts, durations, and zero loaded face
|
|
payloads for the minimal PPI fixture.
|
|
- `pano_cli_save_project_roundtrip_smoke` passed and proves the metadata-only
|
|
`pp_assets` PPI writer can save a generated multi-frame PPI and reload it
|
|
through `pano_cli load-project`.
|
|
- `pano_cli_save_project_payload_roundtrip_smoke` passed and proves the
|
|
`pp_assets` PPI writer can save a compressed RGBA PNG dirty-face payload and
|
|
reload it as decoded `pp_document` face-pixel data.
|
|
- `pano_cli_parse_layout_smoke` passed.
|
|
- `pano_cli_simulate_stroke_smoke` passed and reports deterministic stroke
|
|
sample counts/distances.
|
|
- `pano_cli_simulate_stroke_script_smoke` passed and reports deterministic
|
|
aggregate stroke-script counts/distances.
|
|
- `panopainter_validate_shaders` passed, validating 25 shader programs and 7
|
|
shader includes for stage markers and include graph integrity.
|
|
- `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless,
|
|
and Android arm64 configure/build, covering framebuffer fetch, map-buffer
|
|
alignment, desktop GL core float support, GLES float/half-float extensions,
|
|
WebGL exclusion behavior, upload types for RGBA8/RGBA16F/RGBA32F internal
|
|
formats, image channel-count format mapping including invalid counts, and
|
|
RGBA8/RGBA32F readback format and byte-count mapping, PBO pixel-buffer
|
|
target/usage/access mapping, framebuffer status names, framebuffer blit color
|
|
mask and linear/nearest filters, plus Shape index-type and fill/stroke primitive mode mapping,
|
|
PanoPainter cube-face texture-target order, and the linear clamp-to-edge
|
|
render-target texture parameter set used by `RTT::create`.
|
|
Sampler parameter validation covers wrap S/T/R plus min/mag filter ordering
|
|
used by legacy `Sampler::set` and `Sampler::set_filter`, plus the desktop
|
|
border-color parameter name used by `Sampler::set_border`.
|
|
Shader attribute binding catalog validation covers the current `pos`, `uvs`,
|
|
`uvs2`, `col`, and `nor` bindings and rejects empty, unnamed, null-name, and
|
|
duplicate-name catalogs while preserving legacy shared locations. Shader
|
|
uniform catalog validation covers the 43 legacy uniform
|
|
names used by `Shader`, preserves the legacy hash ids, and rejects empty,
|
|
unnamed, null-name, mismatched-hash, and duplicate-name catalogs.
|
|
- PowerShell analyze automation returns JSON summaries and includes the shader
|
|
validation target and renderer-boundary guard.
|
|
- `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled
|
|
vcpkg root, installed the manifest dependencies, built the headless component
|
|
matrix, and passed `desktop-fast-vcpkg`.
|
|
- `pp_ui_core` built and tested against vcpkg tinyxml2 on
|
|
`windows-msvc-vcpkg-headless` and against the vendored fallback on
|
|
`windows-msvc-default` and `android-arm64`.
|
|
- `windows-clangcl-asan` configures headlessly with clang-cl 18.1.8 and
|
|
release MSVC runtime selection; build remains blocked and debt-tracked in
|
|
DEBT-0014 because the selected VS 2026-preview STL requires Clang 20 or
|
|
newer.
|
|
- `PanoPainter.exe` built through CMake at
|
|
`out/build/windows-msvc-default/Debug/PanoPainter.exe`.
|
|
- PowerShell build/test automation wrappers return JSON summaries and passed
|
|
local smoke checks.
|
|
- Renderer-boundary automation fails if active non-backend source code
|
|
reintroduces raw `GL_*`/`WGL_*` constants outside the allowed legacy OpenGL
|
|
implementation files.
|
|
- `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict
|
|
command-order validation and command/trace capture, giving automation a
|
|
backend-neutral render path that does not require a window or GL context.
|
|
- `pano_cli record-render` exercises that headless recording renderer and emits
|
|
JSON command counts, target dimensions, backend name, and trace/draw summary
|
|
for agent automation.
|
|
- `pano_cli simulate-document-history` exercises pure document history
|
|
apply/undo/redo behavior and emits JSON layer/frame/history state for agent
|
|
automation.
|
|
- `pano_cli simulate-document-edits` exercises pure document layer/frame edit
|
|
operations and emits JSON metadata, frame order, face-payload state, and
|
|
selection-mask state for agent automation.
|
|
- `pano_cli simulate-image-import` exercises embedded PNG decode through
|
|
`pp_assets` and `pp_document` face-payload attachment through JSON
|
|
automation.
|
|
- `pano_cli import-image` accepts a PNG file path, decodes RGBA8 pixels through
|
|
`pp_assets`, attaches them to a pure `pp_document` face payload, and has
|
|
checked-in decodable-PNG plus truncated-PNG rejection smoke tests.
|
|
- `pano_cli export-image` writes deterministic RGBA8 PNGs through `pp_assets`
|
|
and has a save/import round-trip smoke test. Full legacy canvas export
|
|
remains a future `pano_cli` task.
|
|
- `pano_cli save-project` exposes generated multi-layer, multi-frame PPI
|
|
writing with layer metadata through JSON automation and is covered by
|
|
metadata-only and dirty-face-payload save/load round-trip smoke tests. Full
|
|
legacy canvas save parity remains tracked by DEBT-0013.
|
|
- PowerShell package-smoke wrapper validates the Windows CMake app executable
|
|
and runtime `data/` copy.
|
|
- Android arm64 configured with NDK 29.0.14206865 through the platform-build
|
|
wrapper and compiled headless foundation/tool/test targets.
|
|
- Desktop VR drawing now routes generic OpenGL scissor/depth/blend state,
|
|
depth clears, active texture units, and fallback 2D texture unbinds through
|
|
the renderer GL backend mapping; platform VR SDK bridges remain isolated for
|
|
later platform-shell extraction.
|
|
- Canvas mode overlay, mask, and transform paths now route generic OpenGL
|
|
blend/depth state, active texture units, 2D copy targets, and RGBA8
|
|
readback formats through the renderer GL backend mapping.
|
|
- `NodeCanvas` panorama UI rendering now routes sampler defaults, saved
|
|
viewport/clear/blend/depth/scissor state, color clears, active texture units,
|
|
fallback 2D texture unbinds, copy targets, and RGBA8 render-target formats
|
|
through the renderer GL backend mapping.
|
|
- Canvas resource setup now routes stroke-buffer RGBA8/RGBA16F/RGBA32F
|
|
formats, flood-fill texture upload format/type, brush/stencil/mix sampler
|
|
filters and wraps, and cube-strip import channel formats through the renderer
|
|
GL backend mapping. The clamp-to-border sampler wrap is now cataloged and
|
|
tested in `pp_renderer_gl`.
|
|
- Early canvas draw helpers now route pick readbacks, stroke mixer depth/scissor
|
|
and blend state, saved viewport/clear-state queries, active texture units,
|
|
fallback 2D texture unbinds, and stroke background copy targets through the
|
|
renderer GL backend mapping.
|
|
- Canvas stroke commit now routes saved viewport/clear/blend state, history
|
|
readbacks, active texture units, fallback 2D texture unbinds, and layer
|
|
compositing copy targets through the renderer GL backend mapping.
|
|
- Canvas layer merge rendering and explicit layer-merge compositing now route
|
|
depth/blend state, active texture units, fallback 2D texture unbinds, and
|
|
merge framebuffer copy targets through the renderer GL backend mapping.
|
|
- Canvas equirectangular import drawing and depth export rendering now route
|
|
depth/blend state and active texture units through the renderer GL backend
|
|
mapping.
|
|
- Canvas thumbnail generation and object-drawing helpers now route saved
|
|
viewport/clear/blend state, active texture units, readback format/type,
|
|
framebuffer copy targets, and renderbuffer/depth attachment parameters through
|
|
the renderer GL backend mapping; `src/canvas.cpp` no longer contains raw
|
|
`GL_*` constants.
|
|
- Windows desktop OpenGL context creation now consumes a tested
|
|
`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
|
|
entrypoint.
|
|
- Known remaining warnings: legacy project/vendor diagnostics, Visual Studio
|
|
vcpkg-manifest warning, `LNK4099` missing libyuv PDBs, and `LNK4098` runtime
|
|
library conflict from retained vendor binaries.
|
|
|
|
## Current Debt Log
|
|
|
|
The canonical debt log is now `docs/modernization/debt.md`. Keep this section
|
|
as a reminder only; do not add new debt entries here.
|
|
|
|
| ID | Status | Owner | Item | Reason | Validation | Removal Condition |
|
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
| DEBT-0001 | Open | TBD | Existing platform build files remain alongside new CMake | Required for incremental migration | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
|
|
| DEBT-0002 | Open | TBD | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace or document permanent vendored status after vcpkg triplet evaluation |
|
|
| DEBT-0003 | Open | TBD | Existing singletons remain during initial split | Avoid behavior changes while introducing boundaries | App launch and component tests | Replace singleton reaches with context/service injection at component boundaries |
|
|
|
|
## Current Capability Map Seed
|
|
|
|
Use this as the starting checklist for Phase 0 inventory.
|
|
|
|
- Project I/O: PPI open/save, thumbnails, version metadata, autosave/save-as
|
|
flows.
|
|
- Image I/O: JPEG/PNG import/export, cube faces, equirectangular export,
|
|
depth export.
|
|
- Brush system: ABR import, PPBR import/export, presets, tip/pattern/dual brush,
|
|
pressure, jitter, blend modes.
|
|
- Painting: six cube faces, temporary stroke buffers, erase, flood fill, masks,
|
|
alpha lock, layer compositing.
|
|
- Layers and animation: layer add/remove/move/merge, blend/opacity/visibility,
|
|
frame add/remove/duplicate/duration, MP4/timelapse export.
|
|
- UI: XML layout, Yoga layout, panels, dialogs, color tools, brush tools,
|
|
layers, animation timeline, settings, shortcuts, manual/changelog/about.
|
|
- Input: mouse, keyboard, touch, gestures, Wacom tablet, stylus pressure,
|
|
VR controllers.
|
|
- Platform services: clipboard, file picker, save picker, directory picker,
|
|
share/display file, keyboard show/hide.
|
|
- VR/platform variants: OpenVR desktop, Quest, Focus/Wave, Android standard,
|
|
iOS/macOS, Linux, WebGL.
|
|
- Cloud/network: upload, download, browse, license/check flows.
|
|
- Recording/export: PBO readbacks, MP4 encoder, timelapse frames.
|